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

Search dozens of selected web sites for OpenSim and LSL script

New! Script Meta-Search will search thousands of scripts here and at other sites for LSL or Opensim scripts.
Loading

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
Rider Tiger Rider  

Tiger Rider

All in one NPC recorder player. Supports both absolute and relative paths and many new commands

Category: Rider
By : Ferd Frederix
Created: 2013-09-13 Edited: 2013-09-13
Worlds: OpenSim

the Zip file

Download all files for Tiger Rider
Contents are in zip format, with .LSL (text) source code and LSLEdit (text + Solution) formats.
Get file # 1. appearance.txt
Get file # 2. Modded NPC controller.lsl
Get file # 3. Non_Physical car.lsl
Get file # 4. Path.txt
Get file # 5. Poseball.lsl
Get file # 6. Tiger Rider Vehicle Script.lsl
Get file # 7. AO.lsl
Get file # 8. manual animation.lsl
Get file # 9. Notecard.txt
Get file # 10. Rat.txt

This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1
2
3 ////////////////////////////////////////////////////////////////////////////////////
4 // comment these lines out for Opensim, leave uncommented for testing in LSLEditor
5 integer OS_NPC_SIT_NOW = 1;
6 integer OS_NPC_SENSE_AS_AGENT = 2;
7 integer OS_NPC_NO_FLY = 3;
8
9 osNpcStand(key npc) {
10 llOwnerSay("Standing");
11 }
13 vDestPos.x += llFrand(1.0); // some randomness for debugging
14 llOwnerSay("Reached " + (string) vDestPos);
15 return vDestPos;
16 }
17 osNpcMoveToTarget(key npc, vector target, integer options){
18 llSay(0,"Moving to " + (string) target);
19 }
20 key osNpcCreate(string firstname, string lastname, vector position, string cloneFrom) {
21 llSay(0,"Creating NPC " + firstname + " " + lastname + " at " + (string) position);
22 return (key) "12345000-0000-0000-0000-0000000000002";
23 }
24 osNpcLoadAppearance(key npc, string notecard) {
25 llSay(0,"Load notecard " + notecard);
26 }
27 osNpcPlayAnimation(key npc, string animation) {
28 llSay(0,"Playing animation " + animation);
29 }
30 osNpcStopAnimation(key npc, string animation) {
31 llSay(0,"Stopped animation " + animation);
32 }
33 osNpcSay(key npc, integer iChannel, string message) {
34 llSay(0,"Saying " + message);
35 }
36 osNpcWhisper(key npc, integer iChannel, string message) {
37 llSay(0,"Whispering " + message);
38 }
39 osNpcShout(key npc, integer iChannel, string message) {
40 llSay(0,"Shouting " + message);
41 }
42 osNpcSit(key npc, key target, integer options) {
43 llSay(0,"Sat on " +target);
44 }
45 osNpcSetRot(key npc, rotation rot) {
46 llSay(0,"Set rotation of NPC to " + (string) rot);
47 }
48 osAgentSaveAppearance(key avatar, string notecard) {
49 llSay(0,"Created Notecard " + notecard);
50 }
51 osNpcRemove (key target) {
52 llSay(0,"NPC removed");
53 }
54 list osGetAvatarList () {
55 list lStuff = [(key) "12345000-0000-0000-0000-0000000000002", vDestPos, "Digit Gorilla"];
56 return lStuff;
57 }
58 osMakeNotecard(string notecardName, string contents) {
59 llOwnerSay("Make Notecard " + notecardName + "Contents:" + (string) contents);
60 }
62 // sample notecard for testing
63 string str = "@spawn=Digit Gorilla|<645, 128, 25>\n"
64 + "@walk=<645, 120, 25>\n"
65 + "REPEAT\n"
66 + "@cmd=0|Hello on channel 0\n"
67 + "@wander=3|5\n"
68 + "@say=say , walking is so tiresome...\n"
69 + "@whisper=whisper, walking is so tiresome...\n"
70 + "@shout=shout, walking is so tiresome...\n"
71 + "@goto=REPEAT\n"
72 + "@goto=NEXT\n"
73 + "@say=i will never say this...\n"
74 + "NEXT\n"
75 + "@sound=somesound\n"
76 + "@randsound\n"
77 + "@pause=5\n"
78 + "@rotate=90\n"
79 + "@wander=3|1\n"
80 + "@say=Uff, I'm done...\n"
81 + "@delete\n";
82 return str;
83 }
84
85 // END commented code for OpenSim vs Editor environments
86 //******************************************************
87
88
89 // Instructions on how to use this is at http://www.free-lsl-scripts.com/opensim/posts/NPC/
90 // This is an OpenSim-only script.
91 // Author: Ferd Frederix
92
93
94 ////////////////////////////////////////////////////////////////////////////////////////////
95 // Original code is Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
96 ///////////////////////////////////////////////////////////////////////////////////////////
97 // Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
98 // rights of fair usage, the disclaimer and warranty conditions. //
99 ///////////////////////////////////////////////////////////////////////////////////////////
100 // The original NPC controller was from http://was.fm/opensim:npc
101
102 // Extensive additions and bug fixes by Fred Beckhusem, aka Ferd Frederix, fred@mitsi.com
103 // llSensor had two params swapped
104 // @Wander would wander where it had rezzed, not where it was.
105 // There was no 'no_sensor' event in sit, so if a @sit failed, the NPC got stuck
106 // The animation and walks always stopped old, then started new. It should be start new, then stop old so the default stand would be suppressed.
107 // New code:
108 // Merged with new Route recorder and notecard writer
109 // If the NPC failed to reach a destination it never moved on. Added WAIT global to tune this
110 // Exposed many tunable variables and ported the code to LSLEditor.
111 // Added floating point to times in notecard.
112
113 // Added @sound, @randsound, @whisper, @shout, and @cmd controls.
114 //
115 // notecards integers are not floats for better control
116 //
117 // Link Messages may be used to perform external control by injecting @commands into the stream of actions
118 // Example:
119 // To chat something, such as with a chat robot
120 // llMessageLinked(LINK_SET,0,"@npc_say=Hello","");
121
122 // This script assumes that NPCs and OSSl scripting is enabled in the OpenSim configuration.
123 // In order to enable them, the following changes must be made in the OpenSim.ini configuration file:
124 //
125 // ; Turn on OSSL
126 // AllowOSFunctions = true
127 // OSFunctionThreatLevel = Severe
128
129 //[NPC]
130 // ;# {Enabled} {} {Enable Non Player Character (NPC) facilities} {true false}
131 // Enabled = true
132 //
133 // and then the server has to be restarted.
134
135
136 // Commands: All commands begin with an @ sign. All other lines are ignored
137 // @commands may have optional parameters. The syntax is always:
138 // @cmd=parm1|parm2
139 // NaN in the table below meand Not a Number. This means there is no parameter
140
141 //Command First Parameter Second Parameter Description
142 //@spawn name location (vector) Rezzes an NPC with name at a location.
143 //@walk destination (vector) NaN Makes the NPC walk to destination.
144 //@fly destination (vector) NaN Makes the NPC fly to destination.
145 //@land destination (vector) NaN Makes the NPC land at destination.
146 //@say string NaN Makes the NPC speak a phrase.
147 //@whisper string NaN Makes the NPC whisper a phrase.
148 //@shout string NaN Makes the NPC shout a phrase.
149 //@pause seconds (float) NaN Makes the NPC wait for a multiple of seconds.
150 //@wander radius (float) cycles (integer) Makes the NPC wander in radius, for cycles seconds.
151 //@stop NaN NaN Halts the NPC script indefinitely. Can be started with a link message
152 //@delete NaN NaN Removes the NPC.
153 //@animate animation (string) time (float) Makes the NPC trigger the animation animation for time seconds.
154 //@goto label (string) NaN Jump to the label label in the script.
155 //@rotate degrees NaN Rotate the NPC degrees around the Z axis.
156 //@sit primitive name NaN Sit on a primitive with a given name.
157 //@stand NaN NaN If sitting on a primitive, stand up.
158 //@sound sound_name NaN plays a sound from inventory
159 //@randsound NaN NaN Plays a random sound from inventory
160 //@cmd channel (integer) string Says string on channel, for controlling external gadgets
161
162
163
164 //////////////////////////////////////////////////////////
165 // DEBUG //
166 //////////////////////////////////////////////////////////
167
168 integer debug = TRUE; // set to TRUE or FALSE for debug chat on various actions
169 integer Editor = FALSE; // set to to TRUE to working in LSLEditor, FALSE for in-world.
170 integer iTitleText = TRUE; // set to TRUE to see debug in text above the controller
171
172 //////////////////////////////////////////////////////////
173 // TUNABLE CONFIGURATION //
174 //////////////////////////////////////////////////////////
175
176 float MAXDIST = 1.0; // how close a NPC has to get to a dest pos to continue to next state. Do not lower this too much, will also need a faster TIMER
177 float TIMER = 0.5; // how often the system checks the distance traveled
178 integer WANDERRAND = TRUE; // set to TRUE and they will pause during wanders a random number of seconds
179 float WANDERTIME = 1.0; // how long they stand after each @wander,if WANDERRAND is FALSE. If WANDERRAND is TRUE, this is the max time
180 integer WAIT = 5; // wait for this number of seconds for the NPC to reach a destination (for safety)
181 float RANGE = 5.0; // 1 to 96.0 meters - anyone this close to the controller will start NPCS if Sensor button is clicked
182 float REZTIME = 10.0; // wait this lng for NPC to rez in, then start the process
183 string STAND = "Stand"; // the name of the default Stand animation
184 string WALK = "Walk"; // the name of the default Walk animation
185 string FLY = "Fly"; // the name of the default Fly animation
186 string RUN = "Run"; // the name of the default Run animation
187 float OffsetZ = 0.5; // appear 0.5 meter above ground, this is added to all destinations to keep them from sinking in.
188
189 // globals section
190 integer iChannel; // a listen channel, randomly assigned
191 integer iHandle; // the handle to it
192
193 // NPC
194 vector newDest ; // tmp storage for the walks
195 integer iWaitCounter ; // wait for this number of seconds for the NPC to reach a desrtination
196 string sNPCName; // the name of the NPC that may be in world. So we can remove it.
197 integer bNPC_STOP = FALSE; // boolean to reuse a listener
198 integer bForget = FALSE; // set to TRUE by link messages so we do not remember them
199 float fTimerVal ; // how long we wait when wandering (calculated)
200
201 // OS_NPC_CREATOR_OWNED will create an 'owned' NPC that will only respond to osNpc* commands issued from scripts that have the same owner as the one that created the NPC.
202 // OS_NPC_NOT_OWNEDwill create an 'unowned' NPC that will respond to any script that has OSSL permissions to call osNpc* commands.
203 integer NPCOptions = OS_NPC_NOT_OWNED;
204
205
206 integer NPCWalkOption;
207 // Just some notes for what happens to NPCWalkOption:
208 // OS_NPC_FLY - Fly the avatar to the given position. The avatar will not land unless the OS_NPC_LAND_AT_TARGET option is also given.
209 // OS_NPC_NO_FLY - Do not fly to the target. The NPC will attempt to walk to the location. If it's up in the air then the avatar will keep bouncing hopeless until another move target is given or the move is stopped
210 //OS_NPC_LAND_AT_TARGET - If given and the avatar is flying, then it will land when it reaches the target. If OS_NPC_NO_FLY is given then this option has no effect.
211 // OS_NPC_RUNNING - if given, NPC avatar moves at running/fast flying speed, otherwise moves at walking/slow flying speed.
212
213
214 // menus
215 integer showMenu = FALSE; // when we switch states, we need to bring up a menu
216 list lAtButtons = ["Menu","-", "More", "@run","@walk","@fly", "@land","@wander","@sit", "@stand","@animate","@rotate"];
217 list lMenu2 = ["<<", "@comment", "@stop", "@say","@whisper","@shout", "@sound","@randsound", "-", "@cmd", "@pause", "-" ];
218 string sCommand; // place to store a command for two-prompted ones
219 string sParam2; // place to store a prompt for two-prompted ones
220 string priPub = "Owner Only"; // Private or Group
221 key kUserKey; // the person who is controlling the avatar, not the Owner
222
223 // the command lists
224 list lCommands; // commands are stored here
225 list lNPCScript; // Storage for the NPC script.
226 string npcAction; // Storage for the next action. @cmd=0|hello, this becomes @cmd
227 string npcParams; // Storage for the param, @cmd=0|hello, this becomes 0|hello
228
229 // misc vars
230 string sNotecard; // commands are stored here temporarily for dumping
231 vector vWanderPos; // a place to wander to
232 string lastANIM ; // last animation run
233 // Sensor
234 integer avatarPresent = TRUE; // Sensor sets this flag when people are within Range.
235 integer Sensor; // set to true if we are running a Sensor for avatars
236
237 // Coordinate control
238 vector vInitialPos ; // Vector that will be filled by the script with the initial starting position in region coordinates.
239 vector vDestPos = ZERO_VECTOR; // Storage for destination position.
240 string relAbs = "Absolute"; // absolute vs relative positioning
241
242 ///////////////////////////////////////////////////////////////////////////
243 // FUNCTIONS //
244 ///////////////////////////////////////////////////////////////////////////
245
246
247 // DEBUG(string) will chat a string or display it as hovertext if debug == TRUE
248 DEBUG(string str)
249 {
250 if(debug)
251 llOwnerSay(llGetScriptName()+":" + str); // Send the owner debug info so you can chase NPCS
252 if(iTitleText)
253 {
254 llSleep(0.1);
255 llSetText(str,<1.0,1.0,1.0>,1.0); // show hovertext
256 }
257 }
258
259
260 // common subroutines
261
262 // return TRUE if the avatar is owner when private is set, or TRUE if the avatar is in th same group and GROUP is set.
263 integer checkPerms() {
264 if(priPub == "Private") {
266 kUserKey = llDetectedKey(0);
267 return TRUE;
268 }
269 } else {
271 kUserKey = llDetectedKey(0);
272 return TRUE;
273 }
274 }
275 kUserKey = NULL_KEY;
276 return FALSE;
277 }
278
279
280
281 NPCStart(string anim)
282 {
283 DEBUG(" Start Anim: " + anim);
285
286 if(lastANIM != anim) {
287 osNpcPlayAnimation(KeyValueGet("key"), anim);
288
289 if(llStringLength(lastANIM) && llGetInventoryType(lastANIM) == INVENTORY_ANIMATION) {
290 osNpcStopAnimation(KeyValueGet("key"), lastANIM) ;
291 }
292
293 lastANIM = anim;
294 }
295 } else {
296 llSay(DEBUG_CHANNEL, "No animation named " + anim);
297 }
298 }
299
300
301 TimerEvent(float timesent)
302 {
303 //DEBUG("Setting timer: " + (string) timesent);
304 llSetTimerEvent(timesent);
305 }
306
307
308
309 ProcessLink(string str)
310 {
311 if(llGetSubString(str, 0, 0) != "@")
312 return;
313 DEBUG("Processing exern cmd : " + str);
314 bForget = TRUE; // tell the NPCProcess state to forget this command after processing it.
315 lNPCScript = llListInsertList(lNPCScript,[str],0); // add this command to the beginning of the list of commands
316 // NPCStart(STAND);
317 }
318
319 // Kill a NPC by Name
320 Kill(string sNPCName)
321 {
322 list avatars = osGetAvatarList(); // Returns a strided list of the UUID, position, and name of each avatar in the region except the owner.
323
324
325 //DEBUG(llDumpList2String(avatars,","));
326 integer i;
327 integer count;
328 integer j = llGetListLength(avatars);
329 for (; i < j; i++){
330 if(llList2String(avatars,i) == sNPCName){
331 vector v = llList2Vector(avatars,i-1);
332 key target = llList2Key(avatars,i-2); // get the UUID of the avatar
333 osNpcRemove(target);
334 llOwnerSay("Removed " + sNPCName + " at location " + (string) v);
335 count++;
336 }
337 }
338 if(count)
339 llOwnerSay("Removed " + (string) count + " NPC's");
340 else
341 llOwnerSay("Could not locate " + sNPCName);
342 }
343
344
345 // return a String for the position we are at. Strings used as the caller wants strings
346 string Pos()
347 {
348 vector where = llGetPos(); // find the box position
349
350 where.z += OffsetZ; // use the ground position + an offset
351
352 if(Editor)
353 where = <128,128,31 + llFrand(1)>; // center of sim for editing
354
355 // if attached the height will be too high by 1/2 the agent size
356 if(llGetAttached()) {
358 float Z = size.z;
359 where.z -= Z/2;
360 }
361
362 // DEBUG("Pos= " + (string) where);
363 return (string) where;
364 }
365
366 Expire()
367 {
368 llOwnerSay("Menu expired");
369 iHandle = 0;
370 TimerEvent(0.0);
371 state default; // !!! this will not work
372 }
373
374 // setup a menu with a timer for timeouts, called by all make*()
375 menu()
376 {
377 llListenRemove(iHandle);
378 iChannel = llCeil(llFrand(100000) + 20000);
379 iHandle = llListen(iChannel,"","","");
380 TimerEvent(120.0);
381 }
382
383
384 // make a text box
385 makeText(string Param)
386 {
387 menu();
388 llTextBox(kUserKey, Param, iChannel);
389 }
390
391 // top level menu
392 makeMainMenu()
393 {
394 menu();
395 list buttons = ["Appearance","Recording","Save","Help","Reset","Erase RAM", priPub,relAbs,"-","Stop NPC","Sensor","Start NPC"];
396 llDialog(kUserKey,"Choose a command",buttons,iChannel);
397 }
398
399 // programmable menu for @commands
400 makeMenu(list buttons)
401 {
402 menu();
403 llDialog(kUserKey,"Choose a command",buttons,iChannel);
404 }
405
406
407
408
409 // make one or two text boxes with prompts
410 Text(string cmd, string p1, string p2)
411 {
412 sCommand = cmd;
413 sParam2 = "";
415 sParam2 = p2;
416
417 makeText(p1);
418 }
419
420 ProcessSensor(integer n)
421 {
422 if(Sensor && n)
423 avatarPresent = TRUE; // someone is here and we need to tell the system to run
424 else if(Sensor && !n)
425 avatarPresent = FALSE; // someone is not here and we need to tell the system to stop
426 else
427 avatarPresent = TRUE; // someone is effectivly always here
428 }
429
430 vector CirclePoint(float radius) {
431 float x = llFrand(radius *2) - radius; // +/- radius, randomized
432 float y = llFrand(radius *2) - radius; // +/- radius, randomized
433 return <x, y, 0>; // so this should always happen
434 }
435
436 string KeyValueGet(string var) {
437 list dVars = llParseString2List(llGetObjectDesc(), ["&"], []);
438 do {
439 list data = llParseString2List(llList2String(dVars, 0), ["="], []);
440 string k = llList2String(data, 0);
441 if(k != var) jump continue;
442 //DEBUG("got " + var + " = " + llList2String(data, 1));
443 return llList2String(data, 1);
444 @continue;
445 dVars = llDeleteSubList(dVars, 0, 0);
446 } while(llGetListLength(dVars));
447 return "";
448 }
449
450 KeyValueSet(string var, string val) {
451
452 //DEBUG("set " + var + " = " + val);
453 list dVars = llParseString2List(llGetObjectDesc(), ["&"], []);
454 if(llGetListLength(dVars) == 0)
455 {
456 llSetObjectDesc(var + "=" + val);
457 return;
458 }
459 list result = [];
460 do {
461 list data = llParseString2List(llList2String(dVars, 0), ["="], []);
462 string k = llList2String(data, 0);
463 if(k == "") jump continue;
464 if(k == var && val == "") jump continue;
465 if(k == var) {
466 result += k + "=" + val;
467 val = "";
468 jump continue;
469 }
470 string v = llList2String(data, 1);
471 if(v == "") jump continue;
472 result += k + "=" + v;
473 @continue;
474 dVars = llDeleteSubList(dVars, 0, 0);
475 } while(llGetListLength(dVars));
476 if(val != "") result += var + "=" + val;
478 }
479
480
481 KeyValueDelete(string var) {
482 list dVars = llParseString2List(llGetObjectDesc(), ["&"], []);
483 list result = [];
484 list added = [];
485 do {
486 list data = llParseString2List(llList2String(dVars, 0), ["="], []);
487 string k = llList2String(data, 0);
488 if(k == var) jump continue;
489 string v = llList2String(data, 1);
490 if(v == "") jump continue;
491 if(llListFindList(added, (list)k) != -1) jump continue;
492 result += k + "=" + v;
493 added += k;
494 @continue;
495 dVars = llDeleteSubList(dVars, 0 ,0);
496 } while(llGetListLength(dVars));
497 //DEBUG("del " + var );
499 }
500
501
502 // clear RAM
503 Clr() {
504
505 lCommands = [];
506 llOwnerSay("RAM Memory cleared. Notecards, if any, are not modified.");
507 makeMainMenu();
508 }
509
510 integer checkNoteCards()
511 {
512 // Check that they have saved an Appeaance and Path notecard
513 integer num = llGetInventoryNumber(INVENTORY_NOTECARD); // how many notecards overall
514
515 integer i;
516 integer count;
517 for (; i < num; i++){
519 count++;
520 if(llGetInventoryName(INVENTORY_NOTECARD,i) == "Appearance")
521 count++;
522 }
523 // if we have both, run the NPC
524 return count;
525 }
526
527 // Notes:
528 // No llResetScript() used so we can retain memory between rezzes and sim restarts. NPC info and stateful info is held in the Description
529 //
530
531
532 // This state is the first main menu
533 default
534 {
536 {
537 osNpcRemove(KeyValueGet("key"));
538
539 string rA = KeyValueGet("co"); // Get the remembered menu setting for Abs Vs Relative
540 if(rA == "A")
541 relAbs = "Absolute";
542 else if(rA == "R")
543 relAbs = "Relative";
544 else
545 relAbs = "Absolute";
546
547 if(showMenu)
548 makeMainMenu();
549 llSetText("",<1,1,1>,1.0); // clr all hovertext in ase we are not using it.
550 }
551
552 on_rez(integer param) {
553
554 }
555
556 touch_start(integer n) { // if touched, make a menu
557 if(checkPerms())
558 makeMainMenu();
559 }
560
561 // no changed event needed
562
563 // menu listener
564 listen(integer iChannel, string name, key id, string message) {
565 TimerEvent(0.0); /// kill the menu expiration timer
566
567 if(message == "Stop NPC")
568 {
569 if(llStringLength(sNPCName)){
570 Kill(sNPCName);
571 } else {
572 bNPC_STOP = TRUE;
573 makeText("Enter name of an NPC to stop");
574 }
575 }
576 else if(message == "Erase RAM"){
577 Clr();
578 }
579 else if(message == "Relative"){
580 relAbs = "Absolute";
581 KeyValueSet("co","A"); // remember coordinates = A
582 Clr();
583 }
584 else if(message == "Absolute"){
585 relAbs = "Relative";
586 KeyValueSet("co","R"); // remember coordinates = R
587 Clr();
588 }
589 else if(message == "Recording"){
590 state Commands; // show them the recording menu
591 }
592 else if(message == "Owner Only") {
593 priPub = "Group";
594 llOwnerSay("Group members have control");
595 makeMainMenu();
596 }
597 else if(message == "Group") {
598 priPub = "Owner Only";
599 llOwnerSay("Only you have control");
600 makeMainMenu();
601 }
602 else if(message == "Reset"){
604 }
605 else if(message == "Sensor") {
606 integer count = checkNoteCards();
607
608 if(count == 2) {
609 ProcessSensor(1); // fake 1 avatar to get it rezzed
610 Sensor = TRUE; // we need to scan for avatars
611 state NPCGo;
612 }
613
614 // lslEditor does not handle the above, so I hack it in
615 if(Editor) {
616 Sensor = TRUE; // we need to scan for avatars
617 state NPCGo;
618 }
619
620 llOwnerSay("You have not saved a recording and/or appearance, so we cannot start a NPC");
621 makeMainMenu();
622 }
623 else if(message == "Appearance") {
624 llRemoveInventory("Appearance"); // delete the notecard
625 osAgentSaveAppearance(kUserKey, "Appearance"); // make the ntecard
626 llOwnerSay("Your Appearance has been recorded in notecard 'Appearance'");
627 makeMainMenu();
628 }
629 else if(message == "Save") {
630
631 if(llGetAttached()) {
632 llOwnerSay("Detach the HUD and put it where you want the NPC to appear, then click Start");
633 return;
634 }
635 if(llGetListLength(lCommands) == 0) {
636 llOwnerSay("Nothing recorded, you need to make some Recodings first");
637 makeMainMenu();
638 return;
639 }
640 state Save;
641 }
642 else if(message == "Help"){
643 llLoadURL(kUserKey,"Click to view help","http://www.free-lsl-scripts.com/opensim/posts/NPC/");
644 makeMainMenu();
645 }
646 else if(message == "Start NPC") {
647 integer count = checkNoteCards();
648 if(Editor) state NPCGo;
649 if(count == 2)
650 state NPCGo;
651
652 llOwnerSay("You have not saved a either recording or and appearance, so we cannot start a NPC");
653 makeMainMenu();
654 }
655 else if(bNPC_STOP){
656 bNPC_STOP = FALSE;
657 Kill(message);
658 makeMainMenu();
659 }
660 }
661 timer(){
662 Expire();
663 }
664 }
665
666
667 // This state is used to save a Path notecard
668 state Save
669 {
671 makeText("Stand where you want the NPC to appear, and enter the NPC Name");
672 }
673 listen(integer iChannel, string name, key id, string message) {
674 TimerEvent(0.0); /// kill the menu expiration timer
675
676 sNPCName = message; // in case we need to kill it.
677 vector vDest = Pos();
678
679 if(relAbs == "Relative")
680 {
681 vDest -= llGetPos(); // just an offset for relative
682 }
683 sNotecard = "@spawn=" + message + "|" + (string) vDest + "\n";
684 integer i;
685 integer j = llGetListLength(lCommands);
686 for (; i < j; i++){
687 // get the command to save to the notecard
688 string line = llList2String(lCommands,i);
689 if(relAbs == "Absolute") {
690 sNotecard += line; // add the un-modified string to the notecard
691 } else {
692 // since we have to record absolute coords since we do not know whwre the box goes until they press Save,
693 // we process the absolute to relative conversion for walks here
694 list parts = llParseString2List(line,["="],[]); //get the @command
695
696 if(llList2String(parts,0) == "@walk") {
697 vector vec = (vector) llList2String(parts,1) - llGetPos();
698 sNotecard += "@walk=" + (string) vec + "\n";
699 }
700 else {
701 sNotecard += line; // add the un-modified string to the notecard
702 }
703 }
704 }
705 llRemoveInventory("Path"); // delete the old notecard
706 osMakeNotecard("Path",sNotecard); // Makes the notecard.
707 llOwnerSay("'Path' notecard has been written");
708 state default;
709 }
710 timer(){
711 Expire();
712 }
713
714 }
715
716 // This state is used to record the path for the NPC
717 // Each command can take 0, 1, or 2 params
718 state Commands
719 {
720 state_entry() {
721 makeMenu(lAtButtons);
722 }
723
724 on_rez(integer p) {
726 }
728 if(checkPerms())
729 makeMenu(lAtButtons);
730 }
731
732 listen(integer iChannel, string name, key id, string message)
733 {
734 TimerEvent(0.0); /// kill the menu expiration timer
735
736 if(message == "Menu"){
737 showMenu= TRUE;
738 state default;
739 }
740 else if(message == "More"){
741 makeMenu(lMenu2);
742 }
743 else if(message == "<<") {
744 makeMenu(lAtButtons);
745 }
746 else if(message == "@comment"){
747 Text("# ","Enter a comment","");
748 }
749 else if(message == "@stop"){
750 lCommands += "@stop"+ "\n";
751 makeMenu(lAtButtons);
752 }
753 else if(message == "@run"){
754 lCommands += "@run=" + Pos() + "\n";
755 llOwnerSay("Recorded position: " + Pos());
756 makeMenu(lAtButtons);
757 }
758 else if(message == "@fly"){
759 lCommands += "@fly=" + Pos() + "\n";
760 llOwnerSay("Recorded position: " + Pos());
761 makeMenu(lAtButtons);
762 }
763 else if(message == "@land"){
764 lCommands += "@land=" + Pos() + "\n";
765 llOwnerSay("Recorded position: " + Pos());
766 makeMenu(lAtButtons);
767 }
768 else if(message == "@walk") {
769 lCommands += "@walk=" + Pos() + "\n";
770 llOwnerSay("Recorded position: " + Pos());
771 makeMenu(lAtButtons);
772 }
773 else if(message == "@stop"){
774 lCommands += "@stop"+ "\n";
775 makeMenu(lAtButtons);
776 }
777 else if(message == "@sound"){
778 Text("@sound=","Enter a sound name or UUID to trigger","");
779 }
780 else if(message == "@randsound"){
781 lCommands += "@randsound"+ "\n";
782 makeMenu(lAtButtons);
783 }
784 else if(message == "@say") {
785 Text("@say=","Enter what the NPC will say","");
786 }
787 else if(message == "@whisper"){
788 Text("@whisper=","Enter what the NPC will whisper","");
789 }
790 else if(message == "@shout"){
791 Text("@shout=","Enter what the NPC will shout","");
792 }
793
794 else if(message == "@wander") {
795 Text("@wander=","Enter radius to wander","Enter number of wanders");
796 }
797 else if(message == "@pause") {
798 Text("@pause=","Enter time to pause","");
799 }
800 else if(message == "@rotate") {
801 Text("@rotate=","Enter degrees to rotate","");
802 }
803 else if(message == "@sit"){
804 Text("@sit=","Enter name of object to sit on","");
805 }
806 else if(message == "@cmd"){
807 Text("@cmd=","Enter cjhannel to speak on","Enter text to speak");
808 }
809 else if(message == "@stand"){
810 lCommands += "@stand\n";
811 llOwnerSay("Stand Recorded");
812 makeMenu(lAtButtons);
813 }
814 else if(message == "@animate"){
815 Text("@animate=","Enter animation name to play","Enter time to play the animation");
816 }
817 else if(! llStringLength(sParam2)) {
818 lCommands += sCommand + message + "\n";
819 llOwnerSay("Recorded");
820 makeMenu(lAtButtons);
821 }
822 else if(llStringLength(sParam2)){
823 sCommand = sCommand + message + "|";
824 llOwnerSay("Recorded");
825 makeText(sParam2);
826 sParam2 = "";
827 }
828 }
829 timer() {
830 Expire();
831 }
832
833 }
834
835
836 // This state will create an NPC in world
837 state NPCGo {
838 state_entry() {
839 // DEBUG("NPCGo");
840 ProcessSensor(1); // assert that someone is home so we can get rezzed.
841 osNpcRemove(KeyValueGet("key"));
842 TimerEvent(5);
843 }
844 timer() {
845 lNPCScript = llParseString2List(osGetNotecard("Path"), ["\n"], []);
846 if(llGetListLength(lNPCScript) == 0) {
847 llSay(DEBUG_CHANNEL, "No Path notecard found.");
848 TimerEvent(0.0);
849 return;
850 }
851 state ProcessNPCLine;
852 }
853 changed(integer change) {
854 if(change & CHANGED_REGION_START)
855 state ProcessNPCLine;
856 }
857 on_rez(integer num) {
859 }
860 state_exit() {
861 TimerEvent(0.0);
862 }
863 }
864
865
866 // This state loops over the notecard, processing each command
867 state ProcessNPCLine
868 {
870 {
871 // DEBUG("ProcessNPCLine");
872 @ignore;
873
874 string next = llList2String(lNPCScript, 0); // get the next command
875 // DEBUG("Cmd:" + next);
876 lNPCScript = llDeleteSubList(lNPCScript, 0, 0); // delete it
877 if(! bForget) {
878 lNPCScript += next; // put it on the end unless we are told to forget it from a Link Message
879 bForget = FALSE;
880 }
881 if(llGetSubString(next, 0, 0) != "@") jump ignore; // ignore non-@ commands
882 list data = llParseString2List(next, ["="], []);
883 npcAction = llToLower(llStringTrim(llList2String(data, 0), STRING_TRIM));
884 npcParams = llStringTrim(llList2String(data, 1), STRING_TRIM);
885
886 @commands;
887
888 if(! avatarPresent){
889 state nobodyHome;
890 }
891 if(npcAction == "@spawn") {
892 // DEBUG("Spawning");
893 integer lastIdx = llGetListLength(lNPCScript)-1;
894 lNPCScript = llDeleteSubList(lNPCScript, lastIdx, lastIdx); // remove spawn commands, we do them only once
895 list spawnData = llParseString2List(npcParams, ["|"], []);
896 KeyValueSet("name", llList2String(spawnData, 0));
897 list spawnDest = llParseString2List(llList2String(spawnData, 1), ["<", ",", ">"], []);
898 vInitialPos.x = llList2Float(spawnDest, 0);
899 vInitialPos.y = llList2Float(spawnDest, 1);
900 vInitialPos.z = llList2Float(spawnDest, 2);
901 state spawn;
902 }
903 if(npcAction == "@stop") {
904 state stop;
905 }
906 if(npcAction == "@goto") {
907 // DEBUG("goto");
908 integer lastIdx = llGetListLength(lNPCScript)-1;
909 lNPCScript = llDeleteSubList(lNPCScript, lastIdx, lastIdx);
910 // Wind commands till goto label.
911 @wind;
912 string next1 = llList2String(lNPCScript, 0);
913 lNPCScript = llDeleteSubList(lNPCScript, 0, 0);
914 lNPCScript += next1;
915 if(next1 != npcParams) jump wind;
916 // Wind the label too.
917 next1 = llList2String(lNPCScript, 0);
918 lNPCScript = llDeleteSubList(lNPCScript, 0, 0);
919 lNPCScript += next1;
920 // Get next command.
921 list data1 = llParseString2List(next1, ["="], []);
922 npcAction = llToLower(llStringTrim(llList2String(data1, 0), STRING_TRIM));
923 npcParams = llStringTrim(llList2String(data1, 1), STRING_TRIM);
924 // Reschedule.
925 jump commands;
926 }
927
928 if(npcAction == "@sound") {
929 // DEBUG("sound");
930 llTriggerSound(npcParams,1.0);
931 jump ignore; // process next line
932 }
933 if(npcAction == "@randsound") {
934 // DEBUG("random sound");
936 integer rand = llCeil(llFrand(N)) -1; // pick a random sound
938 llTriggerSound(toPlay,1.0);
939 jump ignore; // process next line
940 }
941
942 if(npcAction == "@walk") {
943 // DEBUG(WALK);
944 list dest = llParseString2List(npcParams, ["<", ",", ">"], []);
945 vDestPos.x = llList2Float(dest, 0);
946 vDestPos.y = llList2Float(dest, 1);
947 vDestPos.z = llList2Float(dest, 2);
948
949 if(vDestPos == ZERO_VECTOR) {
950 llSay(DEBUG_CHANNEL,"Bad (zeros) position for @walk");
951 state ProcessNPCLine;
952 }
953
954 NPCWalkOption = OS_NPC_NO_FLY ;
955 state walk;
956 }
957
958 if(npcAction == "@fly") {
959 list dest = llParseString2List(npcParams, ["<", ",", ">"], []);
960 vDestPos.x = llList2Float(dest, 0);
961 vDestPos.y = llList2Float(dest, 1);
962 vDestPos.z = llList2Float(dest, 2);
963
964 if(vDestPos == ZERO_VECTOR) {
965 llSay(DEBUG_CHANNEL,"Bad (zeros) position for @walk");
966 state ProcessNPCLine;
967 }
968
969 NPCWalkOption = OS_NPC_FLY ;
970 state walk;
971 }
972
973 if(npcAction == "@run") {
974 list dest = llParseString2List(npcParams, ["<", ",", ">"], []);
975 vDestPos.x = llList2Float(dest, 0);
976 vDestPos.y = llList2Float(dest, 1);
977 vDestPos.z = llList2Float(dest, 2);
978
979 if(vDestPos == ZERO_VECTOR) {
980 llSay(DEBUG_CHANNEL,"Bad (zeros) position for @walk");
981 state ProcessNPCLine;
982 }
983
984 NPCWalkOption = OS_NPC_NO_FLY | OS_NPC_RUNNING;
985 state walk;
986 }
987
988 if(npcAction == "@land") {
989 list dest = llParseString2List(npcParams, ["<", ",", ">"], []);
990 vDestPos.x = llList2Float(dest, 0);
991 vDestPos.y = llList2Float(dest, 1);
992 vDestPos.z = llList2Float(dest, 2);
993
994 if(vDestPos == ZERO_VECTOR) {
995 llSay(DEBUG_CHANNEL,"Bad (zeros) position for @walk");
996 state ProcessNPCLine;
997 }
998
999 NPCWalkOption= OS_NPC_FLY | OS_NPC_LAND_AT_TARGET ;
1000 state walk;
1001 }
1002
1003
1004
1005 // chat commands
1006 // speak in white text
1007
1008 if(npcAction == "@say") {
1009 // DEBUG("say");
1010 osNpcSay(KeyValueGet("key"),0, npcParams);
1011 jump ignore; // process next line
1012 }
1013 if(npcAction == "@shout") {
1014 // DEBUG("shout");
1015 osNpcShout(KeyValueGet("key"),0, npcParams);
1016 jump ignore; // process next line
1017 }
1018 if(npcAction == "@whisper") {
1019 // DEBUG("whisper");
1020 osNpcWhisper(KeyValueGet("key"),0, npcParams);
1021 jump ignore; // process next line
1022 }
1023 // speak a command on a channel, so you can open doors and control stuff.
1024 if(npcAction == "@cmd") {
1025 // DEBUG("cmd");
1026 list dataToSpeak = llParseString2List(npcParams, ["|"], []);
1027 integer iChannel = (integer) llList2String(dataToSpeak,0);
1028 string stringToSpeak = llList2String(dataToSpeak,1);
1029 osNpcSay(KeyValueGet("key"), iChannel, stringToSpeak);
1030 jump ignore; // process next line
1031 }
1032 // stop everything
1033 if(npcAction == "@pause") {
1034 // DEBUG("pause");
1035 KeyValueSet("pause", npcParams);
1036 state pause;
1037 }
1038 if(npcAction == "@wander") {
1039 // DEBUG("wander");
1040 list wanderData = llParseString2List(npcParams, ["|"], []);
1041 KeyValueSet("wd", llList2String(wanderData, 0));
1042 KeyValueSet("wc", llList2String(wanderData, 1));
1043 vDestPos = osNpcGetPos(KeyValueGet("key")); // set the wander start
1044 DEBUG("Starting at " + (string) vDestPos);
1045 state wander;
1046 }
1047 if(npcAction == "@rotate") {
1048 // DEBUG("rotate");
1049 KeyValueSet("rot", npcParams);
1050 state rotate;
1051 }
1052 if(npcAction == "@sit") {
1053 // DEBUG("sit");
1054 KeyValueSet("sit", npcParams);
1055 state sit;
1056 }
1057 if(npcAction == "@stand") {
1058 // DEBUG("stand");
1059 state stand;
1060 }
1061 if(npcAction == "@delete") {
1062 state delete;
1063 }
1064 if(npcAction == "@animate") {
1065 // DEBUG("animate");
1066 list animateData = llParseString2List(npcParams, ["|"], []);
1067 KeyValueSet("an", llList2String(animateData, 0));
1068 KeyValueSet("at", llList2String(animateData, 1));
1069 state animate;
1070 }
1071 llSay(DEBUG_CHANNEL, "ERROR: Unrecognized script line: " + npcAction + "=" + npcParams);
1072 jump ignore;
1073
1074 }
1075 changed(integer change) {
1076 if(change & CHANGED_REGION_START)
1077 state NPCGo;
1078 }
1079 on_rez(integer num) {
1081 }
1082 link_message(integer sender, integer num, string str, key id) {
1083 ProcessLink(str);
1084 state ProcessNPCLine;
1085 }
1086
1087
1088 }
1089
1090
1091 state nobodyHome
1092 {
1093 state_entry() {
1094 // DEBUG("Removing NPC");
1095 osNpcRemove(KeyValueGet("key"));
1096 llSensorRepeat("","",AGENT,RANGE,TWO_PI, 5);
1097 }
1098 sensor(integer n) {
1100 state NPCGo;
1101 }
1102
1103 }
1104
1105
1106 state spawn
1107 {
1108 state_entry() {
1109 // DEBUG("state spawn");
1110 list name = llParseString2List(KeyValueGet("name"), [" "], []);
1111 // notecard is stored as offsets from this box with relative addressing. Convert to absolute
1112 if(relAbs == "Relative"){
1113 vInitialPos += llGetPos();
1114 }
1115
1116 // DEBUG("rez:" + (string) vInitialPos);
1117
1118 KeyValueSet("key", osNpcCreate(llList2String(name, 0), llList2String(name, 1), vInitialPos, "Appearance", NPCOptions)); // no OS_NPC_SENSE_AS_AGENT allowed due to llSensor Use
1119 osNpcLoadAppearance(KeyValueGet("key"), "Appearance");
1120 TimerEvent(REZTIME);
1121 NPCStart(STAND);
1122 }
1123 link_message(integer sender, integer num, string str, key id) {
1124 ProcessLink(str);
1125 state ProcessNPCLine;
1126 }
1127 timer() {
1128 KeyValueDelete("name");
1129 state ProcessNPCLine;
1130 }
1131 changed(integer change) {
1132 if(change & CHANGED_REGION_START)
1133 state NPCGo;
1134 }
1135 on_rez(integer num) {
1137 }
1138 state_exit(){
1139 TimerEvent(0.0);
1140 }
1141
1142
1143 }
1144
1145
1146 state rotate {
1147 state_entry() {
1148 // DEBUG("state rotate");
1149 osNpcSetRot(KeyValueGet("key"), llEuler2Rot(<0,0,(float)KeyValueGet("rot")> * DEG_TO_RAD));
1150 TimerEvent(TIMER);
1151 }
1152 link_message(integer sender, integer num, string str, key id) {
1153 ProcessLink(str);
1154 state ProcessNPCLine;
1155 }
1156 timer() {
1157 KeyValueDelete("rot");
1158 state ProcessNPCLine;
1159 }
1160 changed(integer change) {
1161 if(change & CHANGED_REGION_START)
1162 state NPCGo;
1163 }
1164 on_rez(integer num) {
1166 }
1167 state_exit() {
1168 TimerEvent(0.0);
1169 }
1170
1171
1172 }
1173
1174 state sit {
1175 state_entry() {
1176 DEBUG ("state sit on " + KeyValueGet("sit"));
1177
1178 osNpcSit(KeyValueGet("key"), llGetKey(), OS_NPC_SIT_NOW);
1179 KeyValueDelete("sit");
1180 state ProcessNPCLine;
1181 }
1182 link_message(integer sender, integer num, string str, key id) {
1183 ProcessLink(str);
1184 state ProcessNPCLine;
1185 }
1186 changed(integer change) {
1187 if(change & CHANGED_REGION_START)
1188 state NPCGo;
1189 }
1190 on_rez(integer num) {
1192 }
1193 state_exit(){
1194 TimerEvent(0.0);
1195 }
1196
1197 }
1198
1199 state stand {
1200 state_entry() {
1201 // DEBUG("state stand");
1202 osNpcStand(KeyValueGet("key"));
1203 state ProcessNPCLine;
1204 }
1205 changed(integer change) {
1206 if(change & CHANGED_REGION_START)
1207 state NPCGo;
1208 }
1209 on_rez(integer num) {
1211 }
1212
1213 }
1214
1215 state animate {
1216 state_entry() {
1217 // DEBUG("state animate");
1218 if(llGetInventoryType(KeyValueGet("an")) == INVENTORY_ANIMATION) osNpcPlayAnimation(KeyValueGet("key"), KeyValueGet("an"));
1219 TimerEvent((float) KeyValueGet("at"));
1220 }
1221 link_message(integer sender, integer num, string str, key id) {
1222 ProcessLink(str);
1223 state ProcessNPCLine;
1224 }
1225 timer() {
1226 if(llGetInventoryType(KeyValueGet("an")) == INVENTORY_ANIMATION) osNpcStopAnimation(KeyValueGet("key"), KeyValueGet("an"));
1227 KeyValueDelete("an");
1228 KeyValueDelete("at");
1229 state ProcessNPCLine;
1230 }
1231 changed(integer change) {
1232 if(change & CHANGED_REGION_START)
1233 state NPCGo;
1234 }
1235 on_rez(integer num) {
1237 }
1238 state_exit() {
1239 TimerEvent(0.0);
1240 }
1241
1242 }
1243 state delete {
1244 state_entry() {
1245 // DEBUG("state delete");
1246 osNpcRemove(KeyValueGet("key"));
1247 }
1248 link_message(integer sender, integer num, string str, key id) {
1249 if(str == "@npc_start")
1250 {
1251 state NPCGo;
1252 }
1253 }
1254
1255 // No on_rez or changed event needed, the only way out is a link message
1256 }
1257
1258
1259 state walk {
1260 state_entry() {
1261
1262 if(NPCWalkOption & OS_NPC_LAND_AT_TARGET ) {
1263 NPCStart(FLY);
1264
1265 } else if(NPCWalkOption & OS_NPC_RUNNING ) {
1266 NPCStart(RUN);
1267
1268 } else {
1269 NPCStart(WALK);
1270 }
1271
1272 if(Sensor) {
1273 // DEBUG("Sensor on");
1274 llSensor("","",AGENT,RANGE,TWO_PI); // sensor survive state switches.
1275 }
1276
1277 newDest = vDestPos ;
1278 // notecard is stored as offsets from this box with relative addressing. Convert to absolute
1279 if(relAbs == "Relative"){
1280 newDest += llGetPos();
1281 }
1282
1283 DEBUG("Moveto:" + (string) newDest);
1284 iWaitCounter = WAIT; // wait 60 seconds to get to a destination.
1285 osNpcMoveToTarget(KeyValueGet("key"), newDest, NPCWalkOption);
1286 TimerEvent(TIMER);
1287 }
1288 link_message(integer sender, integer num, string str, key id) {
1289 ProcessLink(str);
1290 state ProcessNPCLine;
1291 }
1292 timer() {
1293 if(--iWaitCounter) {
1294
1295 if(llVecDist(osNpcGetPos(KeyValueGet("key")), newDest) > MAXDIST) {
1296 return;
1297 }
1298 }
1299
1300
1301 if(NPCWalkOption & OS_NPC_LAND_AT_TARGET ) {
1302 NPCStart(STAND);
1303 } else if(NPCWalkOption & OS_NPC_RUNNING ) {
1304 NPCStart(STAND);
1305 } else {
1306 NPCStart(STAND);
1307 }
1308
1309 state ProcessNPCLine;
1310 }
1311 sensor(integer n) {
1312 ProcessSensor(n);
1313 }
1314 no_sensor(){
1315 ProcessSensor(0);
1316 }
1317 changed(integer change) {
1318 if(change & CHANGED_REGION_START)
1319 state NPCGo;
1320 }
1321 on_rez(integer num) {
1323 }
1324 state_exit() {
1325 TimerEvent(0.0);
1326 }
1327
1328 }
1329
1330 state wander
1331 {
1332 state_entry() {
1333 DEBUG("state wander");
1334 if(Sensor)
1335 {
1336 // DEBUG("Sensor on");
1337 llSensor("","",AGENT,RANGE,TWO_PI); // sensor survive state switches.
1338 }
1339
1340 vector point = CirclePoint((float)KeyValueGet("wd"));
1341 DEBUG("CirclePoint:" + (string) point);
1342 vWanderPos = vDestPos + point;
1343 DEBUG("vWanderPos:" + (string) vWanderPos);
1344 // notecard is stored as offsets from this box with relative addressing. Convert to absolute
1345 // if(relAbs == "Relative"){
1346
1347 // vWanderPos += llGetPos();
1348 // DEBUG("Relative:" + (string) vWanderPos);
1349 // }
1350
1351 fTimerVal = WANDERTIME; // default time to pause after each wander
1352 if(WANDERRAND)
1353 fTimerVal = llFrand(WANDERTIME); // override, they want random times
1354
1355 NPCStart(WALK);
1356
1357 DEBUG("Wander to:" + (string) vWanderPos);
1358
1359 osNpcMoveToTarget(KeyValueGet("key"), vWanderPos, NPCWalkOption);
1360 iWaitCounter = WAIT; // wait 60 seconds to get to a destination.
1361
1362 TimerEvent(TIMER); // first time we wait for the short timer.
1363 }
1364 link_message(integer sender, integer num, string str, key id) {
1365 ProcessLink(str);
1366 NPCStart(STAND);
1367 state ProcessNPCLine;
1368 }
1369 timer() {
1370
1371 if(--iWaitCounter) // wait 60 seconds to get to a destination.
1372 if(llVecDist(osNpcGetPos(KeyValueGet("key")), vWanderPos) > MAXDIST) return;
1373
1374
1375 // see if wander counter == 0, if so, stop walking, go to stand and process next line
1376 if(KeyValueGet("wc") == "0") {
1377 KeyValueDelete("wc");
1378 KeyValueDelete("wd");
1379 NPCStart(STAND);
1380 state ProcessNPCLine;
1381 }
1382
1383 // subtract 1 from the wander counter
1384 KeyValueSet("wc", (string)((integer)KeyValueGet("wc")-1));
1385
1386 NPCStart(STAND);
1387 state wanderhold;
1388 }
1389 sensor(integer n) {
1390 ProcessSensor(n);
1391 }
1392 no_sensor() {
1393 ProcessSensor(0);
1394 }
1395 changed(integer change) {
1396 if(change & CHANGED_REGION_START)
1397 state NPCGo;
1398 }
1399 on_rez(integer num) {
1401 }
1402 state_exit() {
1403 TimerEvent(0.0);
1404 }
1405 }
1406
1407
1408 state wanderhold
1409 {
1410 state_entry(){
1411 // now that we have reached a wander spot, slow the timer down to the desired value
1412 TimerEvent(fTimerVal);
1413 if(Sensor)
1414 {
1415 // DEBUG("Sensor on");
1416 llSensor("","",AGENT,RANGE,TWO_PI); // sensor survive state switches.
1417 }
1418 }
1419 timer() {
1420 state wander;
1421 }
1422 sensor(integer n){
1423 ProcessSensor(n);
1424 }
1425 no_sensor(){
1426 ProcessSensor(0);
1427 }
1428 changed(integer change) {
1429 if(change & CHANGED_REGION_START)
1430 state NPCGo;
1431 }
1432 on_rez(integer num) {
1434 }
1435 state_exit() {
1436 TimerEvent(0.0);
1437 }
1438
1439
1440 }
1441
1442
1443
1444 // @pause=10 will stand for 10 seconds
1445 state pause {
1446 state_entry() {
1447 DEBUG("state pause");
1448 NPCStart(STAND);
1449 if(Sensor)
1450 {
1451 // DEBUG("Sensor on");
1452 llSensor("","",AGENT,RANGE,TWO_PI); // sensor survive state switches.
1453 }
1454 TimerEvent((float)KeyValueGet("pause"));
1455 }
1456 link_message(integer sender, integer num, string str, key id){
1457 ProcessLink(str);
1458 state ProcessNPCLine;
1459 }
1460 timer() {
1461 NPCStart(STAND);
1462 KeyValueDelete("pause");
1463 state ProcessNPCLine;
1464 }
1465 sensor(integer n)
1466 {
1467 ProcessSensor(n);
1468 }
1469 no_sensor()
1470 {
1471 ProcessSensor(0);
1472 }
1473 changed(integer change) {
1474 if(change & CHANGED_REGION_START)
1475 state NPCGo;
1476 }
1477 on_rez(integer num) {
1479 }
1480 state_exit()
1481 {
1482 TimerEvent(0.0);
1483 }
1484 }
1485
1486 // @stop makes the NPC stand there. You have to linkmessage to get moving again
1487 state stop {
1488 state_entry() {
1489 NPCStart(STAND);
1490 DEBUG("Stopped");
1491 }
1492 link_message(integer sender, integer num, string str, key id){
1493 ProcessLink(str);
1494 state ProcessNPCLine;
1495 }
1496 changed(integer change) {
1497 if(change & CHANGED_REGION_START)
1498 state NPCGo;
1499 }
1500 on_rez(integer num) {
1502 }
1503 }

Tiger Rider

PoseBall for back of rider

Category: Rider
By : Ferd Frederix
Created: 2013-09-13 Edited: 2013-09-13
Worlds: OpenSim


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1
2 key agent;
3 integer debug = TRUE; // set to TRUE or FALSE for debug chat on various actions
4
5 DEBUG(string str)
6 {
7 if(debug)
8 llOwnerSay( str); // Send the owner debug info so you can chase NPCS
9 }
10
11
12 default
13 {
15 {
17 llSay(0, "Hello, sit on me!");
19 }
20
22 {
24 }
25
26 changed(integer change)
27 {
28 if(change & CHANGED_LINK)
29 {
30 agent = llAvatarOnSitTarget();
31 if(agent != NULL_KEY)
32 {
33 if( (agent != llGetOwner()) )
34 {
35 llUnSit(agent);
36 llSay(0,"Sorry, you cannot ride the tiger. Get your own tiger in the Virunga Mountains in OSGrid");
37 DEBUG("Non-owner Seated");
38 }
39 else
40 {
41 DEBUG("Seated");
42 llMessageLinked(LINK_SET,0,"Seated",agent);
44
45 }
46
47 }
48 else
49 {
51 llMessageLinked(LINK_SET,0,"Unseated","");
52 }
53 }
54 }
55
56 }

Tiger Rider

Vehicle code for the Tiger Rider.

Category: Rider
By : Ferd Frederix
Created: 2013-09-13 Edited: 2013-09-13
Worlds: OpenSim


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1
2 ////////////////////////////////////////////////////////////////////////////////////
3 // comment these lines out for Opensim, leave uncommented for testing in LSLEditor
4 integer OS_NPC_SIT_NOW = 1;
5 integer OS_NPC_SENSE_AS_AGENT = 2;
6 integer OS_NPC_NO_FLY = 3;
7 vector vDestPos;
8
9 osNpcStand(key npc) {
10 llOwnerSay("Standing");
11 }
13 vDestPos.x += llFrand(1.0); // some randomness for debugging
14 llOwnerSay("Reached " + (string) vDestPos);
15 return vDestPos;
16 }
17 osNpcMoveToTarget(key npc, vector target, integer options){
18 llSay(0,"Moving to " + (string) target);
19 }
20 key osNpcCreate(string firstname, string lastname, vector position, string cloneFrom, integer option) {
21 llSay(0,"Creating NPC " + firstname + " " + lastname + " at " + (string) position);
22 return (key) "12345000-0000-0000-0000-0000000000002";
23 }
24 osNpcLoadAppearance(key npc, string notecard) {
25 llSay(0,"Load notecard " + notecard);
26 }
27 osNpcPlayAnimation(key npc, string animation) {
28 llSay(0,"Playing animation " + animation);
29 }
30 osNpcStopAnimation(key npc, string animation) {
31 llSay(0,"Stopped animation " + animation);
32 }
33 osNpcSay(key npc, integer iChannel, string message) {
34 llSay(0,"Saying " + message);
35 }
36 osNpcWhisper(key npc, integer iChannel, string message) {
37 llSay(0,"Whispering " + message);
38 }
39 osNpcShout(key npc, integer iChannel, string message) {
40 llSay(0,"Shouting " + message);
41 }
42 osNpcSit(key npc, key target, integer options) {
43 llSay(0,"Sat on " +target);
44 }
45 osNpcSetRot(key npc, rotation rot) {
46 llSay(0,"Set rotation of NPC to " + (string) rot);
47 }
48 osAgentSaveAppearance(key avatar, string notecard) {
49 llSay(0,"Created Notecard " + notecard);
50 }
51 osNpcRemove (key target) {
52 llSay(0,"NPC removed");
53 }
54 list osGetAvatarList () {
55 list lStuff = [(key) "12345000-0000-0000-0000-0000000000002", vDestPos, "Digit Gorilla"];
56 return lStuff;
57 }
58 osMakeNotecard(string notecardName, string contents) {
59 llOwnerSay("Make Notecard " + notecardName + "Contents:" + (string) contents);
60 }
62 // sample notecard for testing
63 string str = "@spawn=Digit Gorilla|<645, 128, 25>\n"
64 + "@walk=<645, 120, 25>\n"
65 + "REPEAT\n"
66 + "@cmd=0|Hello on channel 0\n"
67 + "@wander=3|5\n"
68 + "@say=say , walking is so tiresome...\n"
69 + "@whisper=whisper, walking is so tiresome...\n"
70 + "@shout=shout, walking is so tiresome...\n"
71 + "@goto=REPEAT\n"
72 + "@goto=NEXT\n"
73 + "@say=i will never say this...\n"
74 + "NEXT\n"
75 + "@sound=somesound\n"
76 + "@randsound\n"
77 + "@pause=5\n"
78 + "@rotate=90\n"
79 + "@wander=3|1\n"
80 + "@say=Uff, I'm done...\n"
81 + "@delete\n";
82 return str;
83 }
84
85 // END commented code for OpenSim vs Editor environments
86 //******************************************************
87
88
89 integer debug = TRUE; // set to TRUE or FALSE for debug chat on various actions
90 integer NPC = TRUE; // set to TRUE to get a NPC
91
92 // OpenSim & ODE
93
94 // avatar control animations
95 string AvatarSit = "Ride_Tiger";
96 string AvatarWalk = "Ride_Tiger";
97
98 // NOPC animations
99
100 string NPCSit = "Stand";
101 string NPCStand = "Stand";
102 string NPCWalk = "Walk";
103
104 string whatsplaying = ""; // the currentply playing NPC automation
105
106 integer Private = 0; // Change to 1 to prevent others riding.
107
108 vector Sitpos = <0.35,0,0.35>;
109 vector SitrotV = <0,-20,0>;
110 rotation Sitrot;
111 integer animation_allowed = FALSE;
112 key Agent;
113
114 float forward_power = 16; //Power used to go forward (1 to 30)
115 float reverse_power = -8; //Power ued to go reverse (-1 to -30)
116 float turning_ratio = 1.5; //How sharply the vehicle turns. Less is more sharply. (.1 to 10)
117
118 integer turncount;
119
120 float Speed;
121 integer Run;
122 string last_animation; // the last animation played on the owner
123 string sit_message = "Ride"; //Sit message
124
125
126 DEBUG(string str)
127 {
128 if(debug)
129 llOwnerSay(llGetScriptName()+":" + str); // Send the owner debug info so you can chase NPCS
130 }
131
132
133 SetMaterial()
134 {
136 llMessageLinked(LINK_ALL_OTHERS, 0, "SetMat", NULL_KEY); // Tell daughter pims on ground to be glass
137 }
138
139 // pipeline for animations for the owner
140 AvatarAnimate(string animation)
141 {
142 if(animation != last_animation) {
143 llStartAnimation(animation);
144 llStopAnimation(last_animation);
145 last_animation = animation;
146 }
147 }
148
149 // and for the NPC
150 NPCPlay(string what, float howlong)
151 {
152 if(whatsplaying != what) {
153 DEBUG("Playing NPC animation " + what);
154
155 if(NPC) osNpcPlayAnimation(KeyValueGet("key"), what);
156 if(NPC) osNpcStopAnimation(KeyValueGet("key"), whatsplaying);
157 llSleep(howlong);
158 whatsplaying = what;
159 }
160 }
161
162 // Make the mouth move and play a sound
163 Growl()
164 {
165 DEBUG("Growl");
166 llTriggerSound("Tiger growl 1",1.0);
167 NPCPlay("Tiger_Growl" ,5);
168 }
169
170
171
172
173 setVehicle()
174 {
175 //car
181 llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, 1.0);
184 llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 0.1);
189 }
190
191
192 Init()
193 {
194
196 vector here = llGetPos();
197 // float h = llGround(<0,0,0>) + 0.52;
198 vector rotv = llRot2Euler(llGetRot());
199 rotation rot = llEuler2Rot(<0,0,rotv.z>);
200 // llSetPos(<here.x, here.y,h>);
201 llSetRot(rot);
202 Sitrot = llEuler2Rot(DEG_TO_RAD * SitrotV);
204 Run = 0;
205 }
206
207 string KeyValueGet(string var) {
208 list dVars = llParseString2List(llGetObjectDesc(), ["&"], []);
209 do {
210 list data = llParseString2List(llList2String(dVars, 0), ["="], []);
211 string k = llList2String(data, 0);
212 if(k != var) jump continue;
213 //DEBUG("got " + var + " = " + llList2String(data, 1));
214 return llList2String(data, 1);
215 @continue;
216 dVars = llDeleteSubList(dVars, 0, 0);
217 } while(llGetListLength(dVars));
218 return "";
219 }
220
221
222 KeyValueSet(string var, string val) {
223
224 //DEBUG("set " + var + " = " + val);
225 list dVars = llParseString2List(llGetObjectDesc(), ["&"], []);
226 if(llGetListLength(dVars) == 0)
227 {
228 llSetObjectDesc(var + "=" + val);
229 return;
230 }
231 list result = [];
232 do {
233 list data = llParseString2List(llList2String(dVars, 0), ["="], []);
234 string k = llList2String(data, 0);
235 if(k == "") jump continue;
236 if(k == var && val == "") jump continue;
237 if(k == var) {
238 result += k + "=" + val;
239 val = "";
240 jump continue;
241 }
242 string v = llList2String(data, 1);
243 if(v == "") jump continue;
244 result += k + "=" + v;
245 @continue;
246 dVars = llDeleteSubList(dVars, 0, 0);
247 } while(llGetListLength(dVars));
248 if(val != "") result += var + "=" + val;
250 }
251
252
253 KeyValueDelete(string var) {
254 list dVars = llParseString2List(llGetObjectDesc(), ["&"], []);
255 list result = [];
256 list added = [];
257 do {
258 list data = llParseString2List(llList2String(dVars, 0), ["="], []);
259 string k = llList2String(data, 0);
260 if(k == var) jump continue;
261 string v = llList2String(data, 1);
262 if(v == "") jump continue;
263 if(llListFindList(added, (list)k) != -1) jump continue;
264 result += k + "=" + v;
265 added += k;
266 @continue;
267 dVars = llDeleteSubList(dVars, 0 ,0);
268 } while(llGetListLength(dVars));
269 //DEBUG("del " + var );
271 }
272
273
274
275
276 default
277 {
279 {
280
281 Init();
282 SetMaterial();
283 llSetSitText(sit_message);
284 // forward-back,left-right,updown
285 llSitTarget(Sitpos, Sitrot);
286
287 osNpcRemove(KeyValueGet("key"));
288 if(NPC) KeyValueSet("key", osNpcCreate("Tiger", "Rider", llGetPos(), "Appearance", OS_NPC_SENSE_AS_AGENT)); // no OS_NPC_SENSE_AS_AGENT allowed due to llSensor Use
289 if(NPC) osNpcLoadAppearance(KeyValueGet("key"), "Appearance");
290
291 if(NPC) osNpcSit(KeyValueGet("key"), llGetKey(), OS_NPC_SIT_NOW);
292
293 whatsplaying = NPCSit;
294
295 }
296
297 on_rez(integer rn)
298 {
300 }
301
302 link_message(integer sender_number, integer number, string message, key id)
303 {
304 DEBUG(" Main script heard:" + message + " with key " + (string) id);
305 if(message == "Seated")
306 {
307 Agent = id;
308 setVehicle();
309
310 llSleep(.4);
312 llSleep(.1);
313 Run = 1;
314 llSetTimerEvent(0.3);
316
317 DEBUG("Starting platform");
318 } else if(message == "Unseated") {
319
320 Init(); // shut down physics
322 animation_allowed = FALSE;
323 Run = 0;
324 NPCPlay(NPCSit,1);
325 llSetTimerEvent(0.0);
326 }
327
328 }
329
330 changed(integer change)
331 {
332 if(change & CHANGED_LINK)
333 {
334 key tiger = llAvatarOnSitTarget();
335 if(tiger != NULL_KEY)
336 {
337 if( (tiger != llGetOwner()) )
338 {
339 DEBUG("Tiger Seated");
340 // tiger
341
342 }
343 }
344 }
345 }
346
348 {
349 if(perm)
350 {
351 llOwnerSay("Perms check");
352
354 {
355 llMessageLinked(LINK_SET,0,"PoseOff",""); // show pose ball
356
357
358 Growl();
359
360 NPCPlay(NPCStand,1);
361
364 }
365
367 {
368 animation_allowed = TRUE;
369 AvatarAnimate(AvatarSit);
370 }
371
372 }
373 }
374
375
376 control(key id, integer level, integer edge)
377 {
378 integer reverse=1;
379 vector angular_motor;
380
381 //get current speed
382 vector vel = llGetVel();
383 Speed = llVecMag(vel);
384 //DEBUG((string)Speed);
385
386 //car controls
387 if(level & CONTROL_FWD)
388 {
391 reverse=1;
392 }
393 if(level & CONTROL_BACK)
394 {
397 reverse = -1;
398 }
399
401 {
402 angular_motor.z -= Speed / turning_ratio * reverse;
403 turncount = 10;
404 }
405
407 {
408 angular_motor.z += Speed / turning_ratio * reverse;
409
410 turncount = 10;
411 }
412
414 if(turncount > 0)
415 {
416 turncount--;
417 }
418
419 } //end control
420
421 timer(){
422 if(Run == 1){
423 vector vel = llGetVel();
424
425 Speed = llVecMag(vel);
426 //DEBUG("vecMag Speed " + (string)Speed);
427
428 if(Speed > 0.0)
429 {
430 NPCPlay(NPCWalk,1);
431 AvatarAnimate(AvatarWalk);
432
435 }
436 else {
437 AvatarAnimate(AvatarSit);
438 NPCPlay(NPCStand,1);
439 }
440
441 llSetTimerEvent(0.3); // If restarted timer() appears to keep working
442 }else{
443 llSetTimerEvent(0.0);
444 }
445 }
446
447 } //end default

Tiger Rider

XML apperance notecard for a Tiger in OsGrid

Category: Rider
By : Ferd Frederix
Created: 2013-09-13 Edited: 2013-09-13
Worlds: OpenSim


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 <llsd>
2 <map>
3 <key>serial</key>
4 <integer>1</integer>
5 <key>height</key>
6 <real>1.9283950328826904</real>
7 <key>wearables</key>
8 <array>
9 <array>
10 <map>
11 <key>item</key>
12 <uuid>0a73b6bf-9dd4-490d-be3c-e252c0b9caa1</uuid>
13 <key>asset</key>
14 <uuid>a39940fd-5f56-3930-6ca0-314fa93caec0</uuid>
15 </map>
16 </array>
17 <array>
18 <map>
19 <key>item</key>
20 <uuid>90d332ff-3cb3-41ae-b1f4-3b584067c433</uuid>
21 <key>asset</key>
22 <uuid>f56c71eb-01cb-baf1-7820-f2f763370d95</uuid>
23 </map>
24 </array>
25 <array>
26 <map>
27 <key>item</key>
28 <uuid>e5a4e6e4-b9b3-42d1-b6f1-94718901a8fd</uuid>
29 <key>asset</key>
30 <uuid>b7c68d74-4e2f-4570-baf2-e4eb96bb1e5c</uuid>
31 </map>
32 </array>
33 <array>
34 <map>
35 <key>item</key>
36 <uuid>f28ac7dd-dfff-498a-a080-cea625162861</uuid>
37 <key>asset</key>
38 <uuid>035b3910-4405-6850-db5a-c082f7a42ba4</uuid>
39 </map>
40 </array>
41 <array />
42 <array />
43 <array />
44 <array />
45 <array />
46 <array />
47 <array />
48 <array />
49 <array />
50 <array>
51 <map>
52 <key>item</key>
53 <uuid>66572dda-4f71-4132-848b-79d1ace0b6cf</uuid>
54 <key>asset</key>
55 <uuid>dc0e7357-12bc-7806-eb7e-628e5f6ac937</uuid>
56 </map>
57 </array>
58 <array />
59 </array>
60 <key>textures</key>
61 <array>
62 <uuid>e71b780e-1a57-400d-4649-959f69ec7d51</uuid>
63 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
64 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
65 <uuid>4934f1bf-3b1f-cf4f-dbdf-a72550d05bc6</uuid>
66 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
67 <uuid>4934f1bf-3b1f-cf4f-dbdf-a72550d05bc6</uuid>
68 <uuid>4934f1bf-3b1f-cf4f-dbdf-a72550d05bc6</uuid>
69 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
70 <uuid>3a367d1c-bef1-6d43-7595-e88c1e3aadb3</uuid>
71 <uuid>3a367d1c-bef1-6d43-7595-e88c1e3aadb3</uuid>
72 <uuid>3a367d1c-bef1-6d43-7595-e88c1e3aadb3</uuid>
73 <uuid>3a367d1c-bef1-6d43-7595-e88c1e3aadb3</uuid>
74 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
75 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
76 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
77 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
78 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
79 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
80 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
81 <uuid>c228d1cf-4b5d-4ba8-84f4-899a0796aa97</uuid>
82 <uuid>3a367d1c-bef1-6d43-7595-e88c1e3aadb3</uuid>
83 </array>
84 <key>visualparams</key>
85 <binary encoding="base64">MwqNu3FRAIkWdQCtTCRwXlFCAP8KcMt8P9tqR8wAZv/LrRRmAACZAAB/AAAAf3J/Yz9/jH9/AAAAvwBOFgAAAAAAAAAAkdiFAGgATGMAAIRwslV/f1kvAGTY1szMzDMZWUzMAIQAAGiTd0CgUQB1jn9/h4w7Zlt1Zj9inYKCcj8AAAAAf38AAAAAfwCfAACyf0xVg1Z/fOVjAAAcOAybANbMxgAAiR6E4v/G////////////zAD//////////////wD//////wA/uv8ZZP////9UAAAAfHD///8=</binary>
86 <key>attachments</key>
87 <array>
88 <map>
89 <key>point</key>
90 <integer>5</integer>
91 <key>item</key>
92 <uuid>b2d6ae09-7eb7-4a02-a8df-6086055a6a74</uuid>
93 <key>asset</key>
94 <uuid>41da7995-963d-4011-ac50-389b13df1439</uuid>
95 </map>
96 </array>
97 </map>
98 </llsd>

Tiger Rider

Path notecard

Category: Rider
By : Ferd Frederix
Created: 2013-09-13 Edited: 2013-09-13
Worlds: OpenSim


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 @spawn=Tiger Rider|<0.000000,0.000000,0.500000>
2 @walk=<0.000000,0.1,0.00000>
3 @wander=3|2
4 @walk=<0.000000,0.1,0.00000>
5 @sit=Tiger Rider Platform
6 @sound=Tiger growl 1
7 @animate=Tiger_Growl|4
8 @rotate=90
9 @stop

Tiger Rider

This is the notecard fo r the AO. Delete these comments

Category: Rider
By : Ferd Frederix
Created: 2013-09-13 Edited: 2013-09-13
Worlds: OpenSim


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 ==========================
2 Franimation Overrider v1.7
3 Copyright (C) 2004 Francis Chung
4 Documentation by Gwyneth Llewyn and Kex Godel
5
6 Wet Ikon Distribution
7 ==========================
8
9 Welcome to the wonderful world of animation overriding!
10
11 We hope that you enjoy this open source product.
12
13 Please note that although this object allows you full technical permissions, you must still abide by the licensing terms specified below.
14
15 You are welcome to distribute the Franimation Override script with your products in Second Life, compliant with the Gnu Public License. I would ask that you contact Francis Chung to add yourself to the volounteer support group.
16
17 ----------
18 LICENSEô€€€
19 ----------
20
21 Scripts:
22 ---------
23 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
24
25 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
28
29 Model:
30 ---------
31 The Wet Ikon (amped) model copyright CCA-NoDerivs License.
32
33 ----------------
34 Support Help
35 ----------------
36 The following people have generously offered their time to help with animation overrider questions:
37
38 Ulrika Zugzwang
39 Water Rogers
40 Storma Amarula
41 Torrid Midnight
42 YadNi Monde
43 Wynx Whiplash
44 Gwyneth Llewelyn
45 Email: gwyneth.llewelyn@secondlife.game-host.org
46 MSN: gwyneth.llewelyn@secondlife.game-host.org
47 Yahoo: gwyneth_llewelyn
48
49 -----------------------
50 QUICK START GUIDE
51 -----------------------
52
53 So you're itching to try it out and don't want to read the whole manual? Good for you!
54
55 To get the Franimation Overrider working, there are just a few simple steps. If you need more help, details are provided in the Documentation/FAQ section below.
56
57 1. Rez the Franimation Overrider on the ground.
58
59 2. Drag your custom animations for walking, standing, sitting, etc. and drop them inside the "Contents" folder of the Franimation Overrider.
60
61 3. Open the "*Default Anims" notecard located in the Franimation Overrider and type the EXACT name of each custom animation below every "posture state" line. DO NOT add any lines or remove any lines to any part of the notecard. Just fill in the blank lines. Also note that spaces, punctuation, hyphens, etc. ARE relevant here!
62
63 Example: If you have a custom animation for walking called "Sexy-Walk #1" you have to type exactly that on the line under the "-=Walking=-" posture state keyword. Your notecard should look like the following:
64
65 -=Walking=-
66 Sexy-Walk #1
67 =Running=-
68
69 -=Crouchwalk=-
70 [...]
71
72 4. Attach the Franimation Overrider to yourself.
73
74 5. That's it! Enjoy your "new moves" :-)
75
76
77 -------------------------
78 COMMAND REFERENCE
79 -------------------------
80
81 /ao on
82 Enable the Franimation Overrider
83
84 /ao off
85 Disable the Franimation Overrider. This will make you revert back to using the default animations.
86
87 /ao hide
88 Make the Franimation Overrider attachment invisible (if you don't like it to appear with your current outfit)
89
90 /ao show
91 If invisible, make the Franimation Overrider visible again.
92
93 /ao reset
94 Reset the Franimation Overrider script. This can be useful if the script is acting strangely, or if you just want it to reset and re-read the default notecard.
95
96 /ao nextstand
97 Immediately switches you to the next standing animation in the list, or the first one if you're at the end of the list.
98
99 /ao col [colour]
100 Changes the colour of the model. [colour] can be a name (such as red, tan, or mauve) or a colour vector. (<0,0,1> for blue)
101
102 /animset < NotecardName >
103 This tells the Franimation Overrider to load a different notecard, in case you want to use multiple animation sets and quickly switch between them. You must specify the exact notecard name in place of "< NotecardName >" above.
104
105 /anim < animation name >
106 Play an animation. (eg. /anim hold_r_handgun)
107
108 /noanim < animation name >
109 Stop playing an animation. Play an animation. (eg. /noanim hold_r_handgun)
110
111 /noanim all
112 Stop playing all animations.
113
114 For example, if you want to play a different set of animations when you're tired, you can create a second copy of the Default Animations notecard, name it "sleepyanims" and then switch to it by typing: /animset sleepyanims
115
116
117 ---------------------------
118 DOCUMENTATION / FAQ
119 ---------------------------
120
121 Q> What is "animation overriding"?
122
123 A> This is a way to replace the default body movements that SL does on your avatar with custom animations.
124
125 Your avatar has different "postures", which technically are a "state" you're in, for example, "Standing", "Flying", "Walking", and "Sitting on Ground" are a few of the many posture states your AV can be in.
126
127 An animation overrider makes it possible for you to play a custom animation whenever you are in a specific posture. This means you can choose to replace the way you look when you are standing, flying, sitting, etc.
128
129 This means that you can make your avatar move, stand, and sit in a unique way.
130
131
132 Q> Where are the custom animations?
133
134 A> The Franimation Overrider does not come with any custom animations, you will have to add your own.
135
136
137 Q> Where do I get custom animations?
138
139 A> You can get custom animations in stores, or from friends. There are also a bunch of free animations floating around the world and reportedly available at the Bazaar in Stillman.
140
141 There are nice people collecting dozens of freebie animations, for example, if you look or ask around some people have a "pack" of 200 free custom animations.
142
143 If you need more sophisticated animations, then you may contact one of the many wonderful professional animators out there or buying animations from their stores. You can search for them in world under the "Find" button, with the "Places" tab selected, or search the forums at:
144
145 http://forums.secondlife.com
146
147 If you feel bold, creative, and technically competent, you can try making your own custom animations.
148
149 All custom animations in Second Life were uploaded in "BVH" format from a software program called Poser. You can upload your own if you own a copy of Poser, or if you have any BVH files which are compatible with the exact format that Second Life needs. Most random BVH files will not work without first being processed through Poser with the P2 figure.
150
151 There are several public domain, open source or shareware alternatives to Poser, but so far, they all seem to need the final tweaking done in Poser to work correctly in Second Life.
152
153 There are several tutorials online (again, search for that in the forums) for creating your own animations. To help you get started, here is a rough guideline for creating a still standing pose with Poser:
154
155 - Use a P2 model
156 - Create two frames
157 - Set both frames as keyframes
158 - Leave the first frame unchanged (ignore it)
159 - Pose your model in the second frame
160 - Export BVH
161 - Switch to SL and select "Upload Animation" from the "File" menu
162 - In the preview window, check [x] Loop, and set "Ease In" and "Ease Out" both to "0.5" (roughly)
163 - Click the "Play" arrow button to confirm it looks ok
164 - Click the Upload button
165
166 Important Note: Using Poser and getting it to work with SL can be very challenging. We really can't help you overcome it's learning curve in this document alone or through Instant Messages. If you do need help with Poser, *please* ask on the Second Life forum created specifically for animation discussion.
167
168
169 Q> How does the Franimation Overrider work?
170
171 A> The Franimation Overrider detects your posture and plays a custom animation whenever it changes.
172
173 The Franimation Overrider is an attachment containing a script and a configurable notecard. The Franimation Overrider script will read the notecard so that it knows which postures to override animations for, and the names of the animations to use in their place.
174
175 While the Franimation Overrider is attached, it will constantly monitor your posture state. When it sees that your posture has changed, it will send a command to stop playing the current animation and start playing the custom animation you specified in the notecard.
176
177
178 Q> How do I add animations to my Franimation Overrider?
179
180 A> Just drag and drop them into the contents of the Franimation Overrider, as so:
181
182 - Rez your Franimation Overrider on the ground (must be in an area which allows building)
183 - Right click on the Franimation Overrider, choose Edit
184 - In the edit window, if you see a button which says "More >>" in the bottom right corner, click that so that it opens up the edit window to show you more options
185 - You should see several tabs "General", "Object", "Content" and "Texture".
186 - Click on the "Content" tab. You will see some notes and a script, don't mind them just yet.
187 - Now locate the custom animations that you want to use in your inventory, and drag-drop them into the Contents folder
188 - Right click on the Franimation Overrider and choose "Take" to take it back into your inventory
189
190
191 Q> How do I configure the Franimation Overrider notecard?
192
193 A> Follow these instructions:
194
195 - Attach the Franimation Overrider to yourself, or rez it on the ground
196 - Open the contents tab (as described above)
197 - Open and edit the notecard named "*Default Anims".
198 - Reload the notecard by saying, "/animset *Default Anims"
199
200 Here is where you assign a custom animation to each of the possible "anim states". "Standing" is a special state - you can have up to 5 different standing animations, and Franimation Overrider will cycle among those 5 animations occasionally.
201
202 The notecard configuration is rather simple. Lines starting with things like "-=Sitting=-" identify the posture that will be detected; the line immediately following that one will have the NAME of the custom animation that you have dragged into the Franimation Overrider's inventory.
203
204 *CAUTION: It is very important not to add or remove any lines!
205
206 When you first open the notecard, it will have the names of all states, and all lines in-between will be empty. It is important that you only change the empty line between each -=Posture=- line.
207
208 The empty lines should be the name of the animation which will be played for the posture specified immediately above it. Make sure you write the name EXACTLY like the animations's name - with spaces, punctuation, and caps.
209
210 Sometimes the name can be something very weird, or, if you don't have an US keyboard, some characters may be very hard to reproduce. In that case, either change the animation's name _before_ you drag it into the Franimation Overrider's Contents, or you may open up Properties on that custom animation, use the mouse to copy the name, and paste it into the notecard. Just make sure you don't get any extra spaces at the beginning or end of the animation name when pasting - Franimation Overrider needs the EXACT name to work properly (otherwise you'll get some errors like "Couldn't find animation
211 ").
212
213 Example:
214
215 You want to override your default sitting animation with a custom animation called "weird0-S1tDown #345". Your notecard configuration should have something like that:
216
217 [...]
218 -=Hover=-
219
220 -=Sitting=-
221 weird0-S1tDown #345
222 -=PreJump=-
223
224 [...]
225
226 Notice the empty lines after "Hover" and "PreJump", meaning you don't have any custom animations for those, and Franimation Overrider will simply revert to the default animations. Also note it is very important not to add any extra lines anywhere to the notecard.
227
228 USEFUL TIP:
229
230 You should make a copy of a blank notecard first to your inventory, just in case you delete too many lines and can't remember the state's names afterwards. And make a copy of your own, modified notecard - you'll save time when a new version of the Franimation Overrider comes along!
231
232
233 Q> What if I leave a line blank?
234
235 A> That is fine, if you leave a line blank, the animation for that posture will revert back to the default animation.
236
237
238 Q> Why are there 5 stand animations?
239
240 A> The Franimation Overrider will cycle through all five animations every 40 seconds or so. This behavior is much like the default standing animation where you occasionally shift your avatar's body to different positions.
241
242
243 Q> I'm having animation glitches with my vehicle/dance script when the Franimation Overrider is on.
244
245 A> Other scripts which also play custom animations may interfere with the Franimation Overrider. Turn off the Franimation Overrider before you interact with these objects (see command reference for the off command).
246
247 Also see the Drawbacks section below for more details
248
249
250 -----------------
251 TIPS & TRICKS
252 -----------------
253
254 * Proper Application
255
256 Most animation states are rather simple to understand, but some of them are tricky. For example, you can run instead of walking.
257
258 In most cases, the animation for one would not look proper for the other, which is why there usually are two different custom animations for those states (i.e. two different Sexy Walks or Power Walks).
259
260 Using the wrong one generally looks quite weird (i.e. avatars "running in place" or "taking too long of strides").
261
262 Also note that there are two different sitting animations - one for sitting on chairs (actually, any kind of object surface) and the other for sitting on the ground.
263
264 * Smooth Transitions
265
266 If you have an unusual custom walking animation, you probably will want to also customize the Turn Left and Turn Right animations as well. Otherwise your avatar will seem to go through a complex "dance" every time you turn left or right.
267
268 Flying is also complex, since you have things like "hovering" (standing in the air), flying slowly and quickly. You will want to probably choose a set of animations which makes these flow together well. Note that the "Flying slow" posture state is actually quite rarely experienced, unless you have a flawless connection and very high frame rates, but it does happen :)
269
270 Landing is also another problem, since you have soft and hard landings (depending on the speed) and free falling (not flying at all!).
271
272 So for most realistic appearance, this means that you have to "combine" all these animations properly and see how they work together - that's normally not very easy when you just grab a few freebies (or worse, just poses and no real animations). The same, actually, applies to "swimming" - "flying" at the water's surface or below.
273
274 * Idle Standing Cycle:
275
276 Standing is a special case in animations. The Franimation Overrider allows you to have up to 5 different standing animations, and will cycle among them every 40 seconds or so. If you just have one standing animation, be prepared to wait a while until it's displayed!
277
278 * Multi-Animation:
279
280 For maximum flexibility, the Franimation Overrider also allows you to enable *multiple* animation with each posture. To do so, you put each animation name on the line, separated by commas.
281
282 Why would you want to do this? Well, often you will find that a single animation doesn't fit what you want to do because of animation priorities. The problem sometimes is that high priority animations will completely override the lower priority animations.
283
284 If you are able to create your own animations in Poser, you can avoid this problem by breaking up your animation into multiple parts, which customize different parts of your body, then use the Franimation Overrider to re-play all of them together by specifying them as a comma-separated list on the line in the notecard.
285
286 This not only allows you to work around some of the limitations caused by animation priorities, but allows you to extract much more variety out of your animations since you can play them in different "chords".
287
288 * Randomized Animations
289
290 You case separate animations in the notecard by a '|' symbol. The Franimation overrider will randomly choose between '|' a pipe separated list of comments. For example, suppose you have two separate jump animations, "gwinjump2" and "gwinjump4". In the notecard you can enter:
291
292 :: [ Jumping ] ::
293 gwinjump2|gwinjump4
294
295 So when you jump, sometimes you will jump with "gwinjump2" and sometimes you will jump with "gwinjump4". You can also use this trick to increase the number of different stand animations you use.
296
297 * Smooth Transitions (CONTENT CREATORS, PLEASE READ)
298
299 If you are creating and uploading animations, please note the importance of the "Ease In" and "Ease Out" settings. I strongly recommend setting these at 0.5 or higher, otherwise your animations will SNAP into place far too quickly, and it looks very unnatural.
300
301 By default, the preview window is usually set far too low. Every time I upload an animation, I usually set each to 0.5, sometimes higher (such as a slow dance movement where you want a very steady transition), and sometimes lower when applicable (such as the ease-in on a falling-to-the-ground animation).
302
303 ---------------
304 DRAWBACKS
305 ---------------
306
307 The Franimation Overrider is a nifty script, but as it is not part of the native code in the SL Client/Server architecture, you may occasionally encounter some very minor discrepancies from the intended behavior.
308
309 1. Animation Download Lag
310
311 Animations are rendered in the SL client program - and not server-side - so this means that all animations have to be loaded from the server into your program, for all avatars "seeing" you.
312
313 While the default animations are immediately available for everybody (as said, they already come with the SL application), your cool custom animations are not. This means that you'll probably see them working for you, but on a particularly slow sim, others won't see them immediately.
314
315 This is exactly the reason why sometimes you go to a club and nobody seems to be moving. After a while, you see everybody connected to the dance machine moving, and finally the one having dancing bracelets or loading custom dances.
316
317 The Franimation Overrider works the same way. People who haven't recently "seen" your custom animation will have to download it from the servers before they see it in action. This means that probably just after teleport, or when using a "fresh" animation (say, sitting for the first time), people will NOT immediately see your custom animation, but the default one. There is nothing you can do about it, except fretting and wishing that Linden Lab(TM) gets you faster servers :)
318
319 2. Polling Lag
320
321 The way the Franimation Overrider works is by polling your current posture in a loop to retrieve your current state. This means that there will be a slight bit of response time between when you changed postures and when the next polling event occurs.
322
323 3. Conflicts With Other Animating Objects
324
325 The Franimation Overrider only knows how to override the default animation states. It will get into conflict with other objects which change your animation and should be turned off before using the other objects (see command reference for how to turn it off).
326
327 Examples of other objects which will probably interfere with the Franimation Overrider include dance bracelets, dance machines, and vehicles which use custom animations. Some other animation scripts may have some odd behavior as well, such as the hug animation script.
328
329 -------------
330 CHANGELOG
331 -------------
332 1.7: Added randomized animations, added fix for llGetAnimation bug for turning left/right
333
334 ------
335 END
336 ------
337
338 Documentation written by:
339 2004 Oct 17 - Gwyneth Llewely (first draft)
340 2004 Nov 28 - Kex Godel (edited and reformatted)

Tiger Rider

For manual control of an AO

Category: Rider
By : Ferd Frederix
Created: 2013-09-13 Edited: 2013-09-13
Worlds: OpenSim


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 // Manual Animation Script
2 // Copyright (C) 2004 Francis Chung
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
17
18
19 integer VERBOSE = 1;
20 integer listenRelay = 0x80000000;
21
22 integer gotPermission = 0;
23
24 // Stop all active animations
25 stopAllAnimations() {
26 integer i;
27 list animList;
28
29 if( VERBOSE > 1 )
30 llInstantMessage( llGetPermissionsKey(), "Stopping Animations...." );
31
33 for ( i=0; i < llGetListLength(animList); i++) {
34 if( llList2Key(animList,i) != NULL_KEY ) {
35 llStopAnimation( llList2String(animList,i) );
36 if( VERBOSE > 1 )
37 llInstantMessage( llGetOwner(), "Stopping " + llList2String(animList,i) );
38 }
39 }
40
41 if( VERBOSE > 0 )
42 llInstantMessage( llGetPermissionsKey(), "Stopping Animations....Done" );
43 }
44
45 // Check if any of the list of elements causes a prefix match. If they do, return argv[1], otherwise ""
46 // Used for parsing verbal commands
47 string checkMatch( string str, list prefixes ) {
48 integer numElements = llGetListLength( prefixes );
49 integer i;
50 integer lastChar;
51 string curPrefix;
52 string curStr = llToLower( str );
53
54 // Check against all the list to see if the prefix (argv[0]) matches
55 for( i=0; i<numElements; i++ ) {
56 curPrefix = llList2String(prefixes, i);
57 lastChar = llStringLength( curPrefix ) - 1;
58 if( llGetSubString(curStr, 0, lastChar) == curPrefix )
59 return llGetSubString( str, lastChar+1, llStringLength(str) );
60 }
61 return "";
62 }
63
64
65 default {
66 state_entry() {
67 if( llGetAttached() )
69 }
70
73 gotPermission = 1;
74 else
75 gotPermission = 0;
76 }
77
78 on_rez(integer st) {
80 }
81
82 link_message( integer _sender, integer _channel, string _message, key _k ) {
83 if( _channel == listenRelay && gotPermission ) {
84 string match;
85
86 match = checkMatch( _message, ["/anim ", "anim "] );
87 if( match != "" )
88 llStartAnimation( match );
89
90 match = checkMatch( _message, ["/noanim ", "noanim "] );
91 if( match == "all" )
92 stopAllAnimations();
93 else if( match != "" )
94 llStopAnimation( match );
95 }
96 }
97 }

Tiger Rider

ZHAO-II compatible AO for Second Life

Category: Rider
By : Ferd Frederix
Created: 2013-09-13 Edited: 2013-09-13
Worlds: OpenSim


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 // Francis wuz here
2 // For tech support, please contact one of our generous volounteers:
3 // Support Help:
4 // Second Life: Gwyneth Llewelyn
5 // Email: gwyneth.llewelyn@secondlife.game-host.org
6 // MSN: gwyneth.llewelyn@secondlife.game-host.org
7 // Yahoo: gwyneth_llewelyn
8 // Second Life: Ulrika Zugzwang
9
10 // Franimation Overrider v1.5
11 // Copyright (C) 2004 Francis Chung
12 //
13 // This program is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation; either version 2 of the License, or
16 // (at your option) any later version.
17 //
18 // This program is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
22
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
26
27
28 // Special thanks to Gwyneth Llewyn and Kex Godel for their technical
29 // suggestions and contributions, as well as their heroic documentation
30 // efforts.
31
32 // I would also like to take the time here to recognize Archanox Underthorn
33 // as the creator of the original animation override.
34
35 // CONSTANTS
36 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
37 // Default notecard we read on script_entry
38 string defaultNoteCard = "*Rat";
39
40 // Instruction notecard
41 string instructionNoteCard = "*Rat Animations";
42
43 // Anything we hear through a listen() even will be relayed on this linkmessage channel
44 integer listenRelay = 0x80000000;
45
46 // List of all the animation states
47 list animState = ["Sitting on Ground", "Sitting", "Striding", "Crouching", "CrouchWalking",
48 "Soft Landing", "Standing Up", "Falling Down", "Hovering Down", "Hovering Up",
49 "FlyingSlow", "Flying", "Hovering", "Jumping", "PreJumping", "Running",
50 "Turning Right", "Turning Left", "Walking", "Landing", "Standing" ];
51
52 // Animations in which we automatically disable animation overriding
53 // Try to keep this list short, the longer it is the worse it affects our runtime
54 // (Note: This is *almost* constant. We have to type-convert this to keys instead of strings
55 // on initialization - blargh)
56 list autoDisableList = [
57 "3147d815-6338-b932-f011-16b56d9ac18b", // aim_R_handgun
58 "ea633413-8006-180a-c3ba-96dd1d756720", // aim_R_rifle
59 "b5b4a67d-0aee-30d2-72cd-77b333e932ef", // aim_R_bazooka
60 "46bb4359-de38-4ed8-6a22-f1f52fe8f506", // aim_l_bow
61 "9a728b41-4ba0-4729-4db7-14bc3d3df741", // Launa's hug
62 "f3300ad9-3462-1d07-2044-0fef80062da0", // punch_L
63 "c8e42d32-7310-6906-c903-cab5d4a34656", // punch_r
64 "85428680-6bf9-3e64-b489-6f81087c24bd", // sword_strike_R
65 "eefc79be-daae-a239-8c04-890f5d23654a" // punch_onetwo
66 ];
67
68
69 // Index of interesting animations
70 integer noAnimIndex = -1;
71 integer standIndex = 20;
72 integer sittingIndex = 1;
73 integer sitgroundIndex = 0;
74 integer hoverIndex = 12;
75 integer flyingIndex = 11;
76 integer flyingslowIndex = 10;
77 integer hoverupIndex = 9;
78 integer hoverdownIndex = 8;
79 integer waterTreadIndex = 25;
80 integer swimmingIndex = 26;
81 integer swimupIndex = 27;
82 integer swimdownIndex = 28;
83 integer standingupIndex = 6;
84
85 // list of animations that have a different value when underwater
86 list underwaterAnim = [ hoverIndex, flyingIndex, flyingslowIndex, hoverupIndex, hoverdownIndex ];
87
88 // corresponding list of animations that we override the overrider with when underwater
89 list underwaterOverride = [ waterTreadIndex, swimmingIndex, swimmingIndex, swimupIndex, swimdownIndex];
90
91 // list of animation states that we need to stop the default animations for
92 //list stopAnimState = [ "Sitting" ];
93 list stopAnimState = [ ];
94
95 // corresponding list of animations to stop when entering that state
96 //list stopAnimName = [ "sit" ];
97 list stopAnimName = [ ];
98
99 // Lines in the notecards where to grab animation names
100 // This list is indexed the same as list overrides
101 list lineNums = [ 45, // 0 Sitting on Ground
102 33, // 1 Sitting
103 1, // 2 Striding
104 17, // 3 Crouching
105 5, // 4 CrouchWalking
106 39, // 5 Soft Landing
107 41, // 6 Standing Up
108 37, // 7 Falling Down
109 19, // 8 Hovering Down
110 15, // 9 Hovering Up
111 43, // 10 FlyingSlow
112 7, // 11 Flying
113 31, // 12 Hovering
114 13, // 13 Jumping
115 35, // 14 PreJumping
116 3, // 15 Running
117 11, // 16 Turning Right
118 9, // 17 Turning Left
119 1, // 18 Walking
120 39, // 19 Landing
121 21, // 20 Standing 1
122 23, // 21 Standing 2
123 25, // 22 Standing 3
124 27, // 23 Standing 4
125 29, // 24 Standing 5
126 47, // 25 Treading Water
127 49, // 26 Swimming
128 51, // 27 Swim up
129 43 // 28 Swim Down
130 ];
131
132 // This is an ugly hack, because the standing up animation doesn't work quite right
133 // (SL is borked, this has been bug reported)
134 // If you play a pose overtop the standing up animation, your avatar tends to get
135 // stuck in place.
136 // This is a list of anims that we'll stop automatically
137 list autoStop = [ 5, 6, 19 ];
138 // Amount of time we'll wait before autostopping the animation (set to 0 to turn off autostopping )
139 float autoStopTime = 1.5;
140
141 // List of stands
142 list standIndexes = [ 20, 21, 22, 23, 24 ];
143
144 // How long before flipping stand animations
145 float standTimeDefault = 40.0;
146
147 // Command prefixes we accept
148 list loadCmd = [ "animset ", "/animset " ];
149 list animCmd = [ "ao ", "/ao " ];
150
151 // How fast we should poll for changed anims (as fast as possible)
152 // In practice, you will not poll more than 8 times a second.
153 float timerEventLength = 0.001;
154
155 // The key for the typing animation
156 key typingAnim = "c541c47f-e0c0-058b-ad1a-d6ae3a4584d9";
157
158 // Kex likes the following behaviour:
159 // - When you switch to a standing pose, start again from the beginning of the stand list
160 // - when you switch to a standing pose, don't override with an animation for 5 seconds
161 integer kexMode = FALSE;
162 float kexTime = 5;
163
164 // Send a message if we encounter a state we've never seen before
165 integer DEBUG = FALSE;
166
167 // GLOBALS
168 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
169
170 list stands = [ "", "", "", "", "" ]; // List of stand animations
171 integer curStandIndex = 0; // Current stand we're on (indexed [0, numStands])
172 string curStandAnim = ""; // Current Stand animation
173 integer numStands; // # of stand anims we use (constant: ListLength(stands))
174 integer curStandAnimIndex = 0; // Current stand we're on (indexed [0, numOverrides] )
175
176 list overrides = []; // List of animations we override
177 list notecardLineKey = []; // notecard reading keys
178 integer notecardLinesRead; // number of notecard lines read
179 integer numOverrides; // # of overrides (a constant - llGetListLength(lineNums))
180
181 string lastAnim = ""; // last Animation we ever played
182 integer lastAnimIndex = 0; // index of the last animation we ever played
183 string lastAnimState = ""; // last thing llGetAnimation() returned
184
185 float standTime = standTimeDefault; // How long before flipping stand animations
186
187 integer animOverrideOn = TRUE; // Is the animation override on?
188 integer gotPermission = FALSE; // Do we have animation permissions?
189
190 integer listenHandler0; // Listen handlers
191 integer listenHandler1;
192
193 string stopLeftOverAnim = ""; // Hack to get around LSL goofiness
194
195 // CODE
196 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
197
198 // This would be totally unecessary if we had arrays
199 // _source[_index] = newEntry
200 list listReplace ( list _source, list _newEntry, integer _index ) {
201 return llListInsertList( llDeleteSubList(_source,_index,_index), _newEntry, _index );
202 }
203
204 // Find if two lists/sets share any elements in common
205 integer hasIntersection( list list1, list list2 ) {
206 list bigList;
207 list smallList;
208 integer smallListLength;
209 integer i;
210
211 if( llGetListLength( list1 ) <= llGetListLength( list2 ) ) {
212 smallList = list1;
213 bigList = list2;
214 }
215 else {
216 bigList = list1;
217 smallList = list2;
218 }
219 smallListLength = llGetListLength( smallList );
220
221 for ( i=0; i<smallListLength; i++ ) {
222 if( llListFindList( bigList, llList2List(smallList,i,i) ) != -1 ) {
223 return TRUE;
224 }
225 }
226
227 return FALSE;
228 }
229
230 startAnimationList( string csvAnims ) {
231 list anims = llCSV2List( csvAnims );
232 integer numAnims = llGetListLength( anims );
233 integer i;
234 for( i=0; i<numAnims; i++ )
236 }
237
238 stopAnimationList( string csvAnims ) {
239 list anims = llCSV2List( csvAnims );
240 integer numAnims = llGetListLength( anims );
241 integer i;
242 for( i=0; i<numAnims; i++ )
244 }
245
246 startNewAnimation( string _anim, integer _animIndex, string _state ) {
247 if( _anim != lastAnim ) {
248 if( lastAnim != "" )
249 stopAnimationList( lastAnim );
250 if( _anim != "" ) { // Time to play a new animation
251 startAnimationList( _anim );
252
253 if( _state != lastAnimState && llListFindList(stopAnimState, [_state]) != -1 ) {
254 // Stop the default sit/sit ground animation
255 llStopAnimation( llList2String(stopAnimName, llListFindList(stopAnimState, [_state])) );
256 }
257 else if( llListFindList( autoStop, [_animIndex] ) != -1 ) {
258 // This is an ugly hack, because the standing up animation doesn't work quite right
259 // (SL is borked, this has been bug reported)
260 // If you play a pose overtop the standing up animation, your avatar tends to get
261 // stuck in place.
262 if( lastAnim != "" ) {
263 stopAnimationList( lastAnim );
264 lastAnim = "";
265 }
266 llSleep( autoStopTime );
267 stopAnimationList( _anim );
268 }
269 }
270 lastAnim = _anim;
271 }
272 lastAnimIndex = _animIndex;
273 lastAnimState = _state;
274 }
275
276 // Load all the animation names from a notecard
277 loadNoteCard( string _notecard ) {
278 integer i;
279
280 if( llGetInventoryKey(_notecard) == NULL_KEY ) {
281 llSay( 0, "Notecard '" + _notecard + "' does not exist." );
282 return;
283 }
284
285 llInstantMessage( llGetOwner(), "Loading notecard '" + _notecard + "'..." );
286 // Start reading the data
287 notecardLinesRead = 0;
288 notecardLineKey = [];
289 for ( i=0; i<numOverrides; i++ )
290 notecardLineKey += [ llGetNotecardLine( _notecard, llList2Integer(lineNums,i) ) ];
291 }
292
293 // Check if any of the list of elements causes a prefix match. If they do, return argv[1], otherwise ""
294 // Used for parsing verbal commands
295 string checkMatch( string str, list prefixes ) {
296 integer numElements = llGetListLength( prefixes );
297 integer i;
298 integer lastChar;
299 string curPrefix;
300 string curStr = llToLower( str );
301
302 // Check against all the list to see if the prefix (argv[0]) matches
303 for( i=0; i<numElements; i++ ) {
304 curPrefix = llList2String(prefixes, i);
305 lastChar = llStringLength( curPrefix ) - 1;
306 if( llGetSubString(curStr, 0, lastChar) == curPrefix )
307 return llGetSubString( str, lastChar+1, llStringLength(str) );
308 }
309 return "";
310 }
311
312 // Figure out what animation we should be playing right now
313 animOverride() {
314 string curAnimState = llGetAnimation(llGetOwner());
315 integer curAnimIndex;
316 integer underwaterAnimIndex;
317 vector curPos;
318
319 // Hack, because, SL really likes to switch between crouch and crouchwalking for no reason
320 if( curAnimState == "CrouchWalking" ) {
321 if( llVecMag(llGetVel()) < .5 )
322 curAnimState = "Crouching";
323 }
324
325 if( curAnimState == lastAnimState ) {
326 // This conditional not absolutely necessary (In fact it's better if it's not here)
327 // But it's good for increasing performance.
328 // One of the drawbacks of this performance hack is the underwater animations
329 // If you fly up, it will keep playing the "swim up" animation even after you've
330 // left the water.
331 return;
332 }
333
334 curAnimIndex = llListFindList( animState, [curAnimState] );
335 underwaterAnimIndex = llListFindList( underwaterAnim, [curAnimIndex] );
336 curPos = llGetPos();
337
338 if( curAnimIndex == -1 ) {
339 if( DEBUG )
340 llInstantMessage( llGetOwner(), "Unknown animation state '" + curAnimState + "'" );
341 }
342 else if( curAnimIndex == standIndex ) {
343 if( kexMode ) {
344 if( lastAnimIndex != standIndex ) {
345 resetStand();
346 standTime = kexTime;
347 }
348 }
349 startNewAnimation( curStandAnim, curStandAnimIndex, curAnimState );
350 }
351 else {
352 if( underwaterAnimIndex != -1 && llWater(ZERO_VECTOR) > curPos.z )
353 curAnimIndex = llList2Integer( underwaterOverride, underwaterAnimIndex );
354 startNewAnimation( llList2String(overrides,curAnimIndex), curAnimIndex, curAnimState );
355 }
356 }
357
358 // For kexMode
359 // Reset to a non-override (last index)
360 resetStand() {
361 curStandIndex = numStands - 1;
362 curStandAnimIndex = llList2Integer(standIndexes,curStandIndex);
363 curStandAnim = "";
365 }
366
367
368 // Switch to the next stand anim
369 doNextStand() {
370 if( kexMode ) {
371 // Make sure we reset to a reasonable stand time
372 standTime = standTimeDefault;
373 }
374 curStandIndex = (curStandIndex+1) % numStands;
375 curStandAnimIndex = llList2Integer(standIndexes,curStandIndex);
376 curStandAnim = llList2String(overrides, curStandAnimIndex);
377 if( lastAnimState == "Standing" )
378 startNewAnimation( curStandAnim, curStandAnimIndex, lastAnimState );
380 }
381
382 // Returns true if we should override the current animation
383 integer shouldOverride() {
384 if( animOverrideOn && gotPermission ) {
385 // Check if we should explicitly NOT override a playing animation
386 if( hasIntersection( autoDisableList, llGetAnimationList(llGetOwner()) ) ) {
387 startNewAnimation( "", noAnimIndex, "" );
388 return FALSE;
389 }
390 return TRUE;
391 }
392 return FALSE;
393 }
394
395 // Initialize listeners, and reset some status variables
396 initialize() {
397 if( animOverrideOn )
398 llSetTimerEvent( timerEventLength );
399 else
400 llSetTimerEvent( 0 );
401
402 // Stop this animation after we regain animation permissions
403 // LSL can be a bit gooofy, because you can teleport somewhere, and have animation permissions
404 // on arrival. Not entirely sure if this is by design, or what, but I'll use a conservative
405 // work-around to this issue.
406 stopLeftOverAnim = lastAnim;
407
408 lastAnim = "";
409 lastAnimIndex = noAnimIndex;
410 lastAnimState = "";
411 gotPermission = FALSE;
412
413 if( listenHandler0 )
414 llListenRemove( listenHandler0 );
415 listenHandler0 = llListen( 0, "", llGetOwner(), "" );
416 if( listenHandler1 )
417 llListenRemove( listenHandler1 );
418 listenHandler1 = llListen( 1, "", llGetOwner(), "" );
419
420 llInstantMessage( llGetOwner(), (string) llGetFreeMemory() + " bytes free" );
421 }
422
423 // STATE
424 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
425
426 default {
427 state_entry() {
428 integer i;
429
430 if( llGetAttached() )
432
433 // Initialize!
434 numStands = llGetListLength( stands );
435 numOverrides = llGetListLength(lineNums);
436 curStandAnimIndex = llList2Integer(standIndexes,curStandIndex);
437
438 // Type convert strings to keys :P
439 for ( i=0; i<llGetListLength(autoDisableList); i++ ) {
440 key k = llList2Key( autoDisableList, i );
441 autoDisableList = listReplace ( autoDisableList, [ k ], i );
442 }
443
444 // populate override list with blanks
445 for ( i=0; i<numOverrides; i++ ) {
446 overrides += [ "" ];
447 }
448 initialize();
449 loadNoteCard( defaultNoteCard );
450
451 // turn off the auto-stop anim hack
452 if( autoStopTime == 0 )
453 autoStop = [];
454
456 }
457
462 gotPermission = TRUE;
463 if( stopLeftOverAnim != "" )
464 stopAnimationList( stopLeftOverAnim );
465 }
466 }
467
468 attach( key _k ) {
469 if( _k != NULL_KEY )
471 }
472
473 listen( integer _channel, string _name, key _id,string _message ) {
474 string match;
475
476 // Send a link message to all other scripts, so they don't have to use a listen
477 llMessageLinked( LINK_SET, listenRelay, _message, _id );
478
479 match = checkMatch( _message, loadCmd );
480 if( match != "" )
481 loadNoteCard( match );
482 match = checkMatch( _message, animCmd );
483 if( match == "on" ) {
484 llSetTimerEvent( timerEventLength );
485 animOverrideOn = TRUE;
486 if( gotPermission )
487 animOverride();
488 llInstantMessage( llGetOwner(), "Franimation override on." );
489 }
490 else if( match == "off" ) {
491 llSetTimerEvent( 0 );
492 animOverrideOn = FALSE;
493 startNewAnimation( "", noAnimIndex, lastAnimState );
494 llInstantMessage( llGetOwner(), "Franimation override off." );
495 }
496 else if( match == "hide" ) {
498 llInstantMessage( llGetOwner(), "Franimation override set invisible." );
499 }
500 else if( match == "show" ) {
502 llInstantMessage( llGetOwner(), "Franimation override set visible." );
503 }
504 else if( match == "nextstand" ) {
505 if( animOverrideOn && gotPermission )
506 doNextStand();
507 }
508 else if( match == "reset" ) {
510 }
511 }
512
513 dataserver( key _query_id, string _data ) {
514 integer index = llListFindList( notecardLineKey, [_query_id] );
515 if( _data != EOF && index != -1 ) { // not at the end of the notecard and not random crap
516 if( index == curStandAnimIndex ) // Pull in the current stand animation
517 curStandAnim = _data;
518
519 // Whoops, we're replacing the currently playing anim
520 if( animOverrideOn && gotPermission && index == lastAnimIndex ) {
521 integer stopAnim;
522
523 // Better play the new one :)
524 startNewAnimation( _data, lastAnimIndex, lastAnimState );
525
526 // If we're not override an animation we've explicitly stopped, we
527 // had better replay the explicitly stopped animation
528 if( _data != "" ) {
529 stopAnim = llListFindList( stopAnimState, [ lastAnimState ] );
530 if( stopAnim != -1 )
531 llStartAnimation( llList2String(stopAnimName, stopAnim) );
532 }
533 }
534
535 // Store the name of the new animation
536 overrides = listReplace( overrides, [_data], index );
537
538 // See if we're done loading the notecard. Users like status messages.
539 if( ++notecardLinesRead == numOverrides )
540 llInstantMessage( llGetOwner(), "Finished reading notecard. (" +
541 (string) llGetFreeMemory() + " bytes free)" );
542 }
543 }
544
545 on_rez( integer _code ) {
546 initialize();
547 }
548
549 control( key _id, integer _level, integer _edge ) {
550 if( shouldOverride() )
551 animOverride();
552 }
553
554 touch_start( integer _num ) {
555 llGiveInventory( llDetectedKey(0), instructionNoteCard );
556 }
557
558 timer() {
559 if( shouldOverride() ) {
560 animOverride();
561
562 // Is it time to switch stand animations?
563 if( llGetTime() > standTime ) {
564 // Don't interupt the typing animation with a stand change
565 if( llListFindList(llGetAnimationList(llGetOwner()), [typingAnim]) == -1 )
566 doNextStand();
567 }
568 }
569 }
570 }

Tiger Rider

This notecard is the animations for the Tiger Rider

Category: Rider
By : Ferd Frederix
Created: 2013-09-13 Edited: 2013-09-13
Worlds: OpenSim


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 :: [ Walking (also Striding) ] ::
2 TigerRegularWalk
3 :: [ Running ] ::
4 TigerRun
5 :: [ CrouchWalking ] ::
6 TigerRegularWalk
7 :: [ Flying (also FlyingSlow) ] ::
8 posestand-pose
9 :: [ Turning Left ] ::
10 TigerRegularWalk
11 :: [ Turning Right ] ::
12 TigerRegularWalk
13 :: [ Jumping ] ::
14 TigerRegularWalk
15 :: [ Hovering Up ] ::
16 TigerRegularWalk
17 :: [ Crouching ] ::
18 TigerRegularWalk
19 :: [ Fly Down ] ::
20 TigerRegularWalk
21 :: [ Standing 1 ] ::
22 posestand-pose
23 :: [ Standing 2 ] ::
24 posestand-pose
25 :: [ Standing 3 ] ::
26 posestand-pose
27 :: [ Standing 4 ] ::
28 posestand-pose
29 :: [ Standing 5 ] ::
30 posestand-pose
31 :: [ Hovering ] ::
32 posestand-pose
33 :: [ Sitting ] ::
34 posestand-pose
35 :: [ PreJumping ] ::
36 Tigerleap
37 :: [ Falling ] ::
38 posestand-pose
39 :: [ Soft Landing/Landing ] ::
40 posestand-pose
41 :: [ Standing Up (That anim you play after you fall when you stand up and brush yourself off) ] ::
42 Tigerleap
43 :: [ FlyingSlow ] ::
44 posestand-pose
45 :: [ Sitting on Ground ] ::
46 posestand-pose
47 :: [ Floating (Hovering underwater) ] ::
48 posestand-pose
49 :: [ Swimming Forward (Flying underwater) ] ::
50 posestand-pose
51 :: [ Swimming Up (Hover Up underwater) ] ::
52 posestand-pose
53 :: [ Swimming Down (Fly Down underwater) ] ::
54 posestand-pose

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