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
Flying NPC's Opensim Ridable NPC  

Opensim Ridable NPC

A rideable Set of twi prims that makes flying creatures possible

Category: Flying NPC's
By : Ferd Frederix
Created: 2015-03-17 Edited: 2015-04-14
Worlds: OpenSim

the Zip file

Download all files for Opensim Ridable NPC
Contents are in zip format, with .LSL (text) source code and LSLEdit (text + Solution) formats.
Get file # 1. flame whoosh.wav
Get file # 2. Flight Script.lsl
Get file # 3. GrowlSound.wav
Get file # 4. monster-short-roar.wav
Get file # 5. NPC Controller.lsl
Get file # 6. whoosh-puff.wav
Get file # 7. wing-flap-1.wav
Get file # 8. wing-flap-2.wav
Get file # 9. wing-flap-3.wav
Get file # 10. Poseball.lsl

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 My appearance will go here

Opensim Ridable NPC

Rideable NPC dragon ( and other rideable creatures) Original script by Shin Ingen

Category: Flying NPC's
By : Ferd Frederix
Created: 2015-03-17 Edited: 2015-04-14
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
4 // Rev 0.1 2014-12-08 compiles
5 // Rev 0.2 2014-12-09 sound files added
6 // Rev 0.3 2014-12-25 sit fixed
7 // Rev 0.4 2014-12-27 dragon collider activated
8 // Rev 0.5 2015-03-17 Archived version
9
10 //*******************************************************
11 // Original Vehicle by Shin Ingen @ http://ingen-lab.com:8002
12 // LSL | OPENSIM | BulletSim | Varregion
13 // iTEC + e3s + DC (ENGINE SPEED SENSITIVE STEERING WITH DYNAMIC CAMERA)
14 //*******************************************************
15
16 integer gInProduction = TRUE; // Will die if in Production mode and avatar is unseated. Uses a Rezzer
17 integer gDebug = FALSE; // set to TRUE or FALSE for debug chat on various actions
18
19 integer DragonChannelBack = 549898; // channel the dragon talks back on.
20 integer DragonChannel = 549897; // secret channel to comm with Dragons. set to zero to not care about them
21
22
23
24
25 // NPC Sit
26 vector gSitPos = <0.35,0,0.85>;
27 vector gSitRot = <0,1,0>;
28
29
30 string dragonState; // flyingslow, flyingfast, or sitting
31 string whatsplaying; // the currently playing NPC automation
32
33 integer turning ; // set to true if we are turning, otherwaise, false
34
35 // Animation Control
36
37 // NPC animations have times that they run, then they return to a state
38 key gNPCKey;
39
40 string gDragonGrowlAnim = "DragonGrowl";
41 integer gDragonGrowlAnimTimeout = 4;
42
43 string gDragonFlySlowAnim = "DragonFlySlow";
44 integer gDragonFlySlowAnimTimeout = 1;
45
46 string gDragonFlyFastAnim = "DragonFlyFast";
47 integer gDragonFlyFastAnimTimeout = 1;
48
49 string gDragonSitAnim1 = "DragonStand1";
50 integer gDragonSitAnim1Timeout = 1;
51
52 string gDragonSitAnim2 = "DragonStand2";
53 integer gDragonSitAnim2Timeout = 1;
54
55 ///////////////////////// VEHICLE CODE BELOW /////////////////////////////
56 integer gForward; // tru for forward flight, false if backing up Beep Beep
57 integer gSpeeding; // TRUE if we are flying fast
58 integer gPhysEng=1; // 0=ODE| 1=Bullet
59
60 //---HUMAN ANIMATION VARIABLES-----------------------------------------------
61 string gAvatarState; // sitting, right, left
62 string gLastAnimation; // the last animation played on the owner
63
64 string gDrivingAnim = "ride-carousel";
65 string gAvatarSitAnim = "ride-carousel";
66 string gAvatarRAnim = "ride-carousel";
67 string gAvatarLAnim = "ride-carousel";
68
69 //---SOUND VARIABLES---------------------------------------------------
70 string gSoundLoopIdle = "wing-flap-3"; //
71 string gSoundLoopSlow = "wing-flap-2";
72 string gSoundLoopAggressive = "wing-flap-1";
73 string gSoundStop = "monster-short-roar"; // Get off the dragon
74 string gSoundStartup = "monster-short-roar"; // Got permissions
75 string gGrowlSound= "GrowlSound"; // Menu
76 string gFlameSound= "flame whoosh"; // Menu
77 string gSteamSound= "whoosh-puff"; // Menu
78
79
80
81 string gNewSound; // new sound to play
82 string gOldSound;
83 //---------------------------------------------------------------------
84
85
86
87
88 //---PERMISSION VARIABLES---------------------------------------------
89 integer gDrivePermit = 1; // 0=EVERYONE 1=OWNERONLY
90 string gSitMessage = "Ride";
91 string gUrNotAllowedMessage = "Dragon is Locked";
92 vector gSitTarget_Pos = <2.5,1.6,1.9>;
93 key gAgent;
94 integer gRun; //ENGINE RUNNING
95 integer gMoving; //VEHICLE MOVING
96 //integer gIdle;
97 //---END PREMISSION----------------------------------------------------
98
99 //---CAMERA VARIABLES--------------------------------------------------
100 //***CONSTANT**********************************************************
101 integer gCamFixed=0; // INITVAL=0 0=FOLLOW CAM 1=FIXED CAM
102 //integer gCamAct; // INITVAL=1 0=INACTIVE 1=ACTIVE
103 //integer gCamFocLocked; // INITVAL=FALSE (TRUE or FALSE)
104 //integer gCamPoslocked; // INITVAL=FALSE (TRUE or FALSE)
105 //***SLIDERS***********************************************************
106 //float gCamBAng; // INITVAL=2.0 (0 to 180) DEGREES
107 //float gCamBLag; // INITVAL=0.1 (0 to 3) SECONDS
108 //float gCamDist; // INITVAL=8.0 (0.5 to 10) METERS
109 //float gCamFocLag; // INITVAL=0.1 (0 to 3) SECONDS
110 //float gCamFocThresh; // INITVAL=0.5 (0 to 4) METERS
111 //float gCamPitch; // INITVAL=20.0 (-45 to 80) DEGREES
112 //float gCamPoslag; // INITVAL=0.1 (0 to 3) SECONDS
113 //float gCamPosthresh; // INITVAL=0.5 (0 to 4) METERS
114 //vector gCamFocOff; // INITVAL=<0.10,0,0> <-10,-10,-10> to <10,10,10> METERS
115 //----END CAMERA-------------------------------------------------------
116
117 //---iTEC - STOCK ENGINE GLOBAL VARIABLES------------------------------
118 list gTSvarList;
119 float gVLMT=0.90; //INITVAL=0.90
120 float gVLMDT=0.10; //INITVAL=0.10
121 vector gVLFT=<8.0, 3000.0, 8.0>; //INITVAL=<8.0, 3000.0, 8.0>
122 vector gVAFT=<0.10, 0.10, 0.10>; //INITVAL=<0.10, 0.10, 0.10>
123 float gVADE=0.20; //INITVAL=0.20
124 float gVADT=0.50; //INITVAL=0.10
125 float gVAMT=1.0; //INITVAL=0.10
126 float gVAMDT=0.10; //INITVAL=0.10
127 float gVLDE=1.0; //INITVAL=1.0
128 float gVLDT=0.10; //INITVAL=0.10
129 float gVVAE=0.50; //INITVAL=0.50
130 float gVVAT=5.0; //INITVAL=5.0
131 float gVHE=0.0; //INITVAL=0.0
132 float gVHT=0.0; //INITVAL=0.0
133 float gVHH=0.0; //INITVAL=0.0
134 float gVB=0.9; //INITVAL=0.0
135 float gVBE=1.0; //INITVAL=1.0
136 float gVBM=0.5; //INITVAL=0.5
137 float gVBT=0.5; //INITVAL=0.5
138 float gVerticalThrust=7.0;
139 //---------------------------------------------------------------------
140
141 //---iTEC POWERTRAIN + (e3s) GLOBAL VARIABLES -------------------------
142 integer gGear;
143
144 float gGearPower;
145 float gReversePower = -5;
146 list gGearPowerList = [ 5, // HOVER
147 15, // 15-KNOTS
148 30, // 30-KNOTS
149 40, // 40-KNOTS
150 50, // 50-KNOTS
151 60, // 60-KNOTS
152 70, // 70-KNOTS
153 100, // 100-KNOTS
154 150, // 150-KNOTS
155 200, // 200-KNOTS
156 225, // 225-KNOTS
157 250 // 250-KNOTS
158 ];
159
160 float gTurnMulti=1;
161 float gTurnRatio;
162 list gTurnRatioList;
163 string gGearName;
164 list gGearNameList =[ "HOVER",
165 "15-KNOTS",
166 "20-KNOTS",
167 "40-KNOTS",
168 "50-KNOTS",
169 "60-KNOTS",
170 "70-KNOTS",
171 "100-KNOTS",
172 "150-KNOTS",
173 "200-KNOTS",
174 "225-KNOTS",
175 "250-KNOTS"
176 ];
177 float gSpeed=0;
178 //---------------------------------------------------------------------
179
180
181 //---MENU HANDLER------------------------------------------------------
182 list MENU_MAIN = ["-","Align","-", "Steam", "Flame","Growl","-", "Help", "-"]; // up to 12 items in list
183 integer menu_handler;
184 integer menu_channel;
185 //---------------------------------------------------------------------
186
187 //=======================================================================
188 //==== E N D G L O B A L V A R I A B L E D E C L A R A T I O N ====
189 //=======================================================================
190
191 DEBUG(string str)
192 {
193 if(gDebug)
194 llSay(0,llGetScriptName()+":" + str); // Send the owner debug info so you can chase NPCS
195 }
196
197 // pipeline for animations for the owner
198 AvatarAnimate(string animation)
199 {
200 return;
201
202 if(animation != gLastAnimation) {
203 DEBUG("*** Avatar start anim:" + animation);
204 llStartAnimation(animation);
205
206 if(llStringLength(gLastAnimation)) {
207 llStopAnimation(gLastAnimation);
208 DEBUG("stop anim:" + gLastAnimation);
209 }
210 gLastAnimation = animation;
211 }
212 }
213
214 // and for the NPC
215 NPCAnimate(string what, float howlong)
216 {
217 if(whatsplaying != what) {
218 DEBUG("*** NPC start animation " + what + " for " + (string) howlong + " seconds");
219 osNpcPlayAnimation(gNPCKey, what);
220
221 if(llStringLength(whatsplaying)) {
222 DEBUG("Stopping NPC animation " + whatsplaying);
223 osNpcStopAnimation(gNPCKey, whatsplaying);
224 }
225 llSetTimerEvent(howlong);
226 whatsplaying = what;
227 }
228 }
229
230
231 // Get a safe rotation
232 SafeMode() {
233 vector here = llGetPos();
234 vector rotv = llRot2Euler(llGetRot());
235 rotation rot = llEuler2Rot(<0,0,rotv.z>);
236 llSetRot(rot);
237 }
238
239
240 // Make the mouth move and play a sound
241 Growl()
242 {
243 DEBUG("Growl");
244 llTriggerSound(gGrowlSound,1.0);
245 llMessageLinked(LINK_SET,1,"growl","");
246 NPCAnimate(gDragonGrowlAnim ,gDragonGrowlAnimTimeout);
247 }
248
249 // Make the mouth move and play a sound
250 Flame()
251 {
252 DEBUG("Flame");
253 llTriggerSound(gFlameSound,1.0);
254 llMessageLinked(LINK_SET,1,"Flame","");
255 NPCAnimate(gDragonGrowlAnim ,gDragonGrowlAnimTimeout);
256 }
257
258 // Make the mouth move and play a sound
259 Steam()
260 {
261 DEBUG("Steam");
262 llTriggerSound(gSteamSound,1.0);
263 llMessageLinked(LINK_SET,1,"Steam","");
264 NPCAnimate(gDragonGrowlAnim ,gDragonGrowlAnimTimeout);
265 }
266
267
268
269
270 init_TSvar(integer i){
271 if(i==0){
272 gTSvarList = [ 3.0, // how fast to reach max speed
273 0.10, // how fast to reach min speed or zero
274 <7.0,3000.0,100.0>, // XYZ linear friction
275 <0.10,0.10,0.20>, // XYZ angular friction
276 0.90, // how fast turning force is applied
277 0.50, // how fast turning force is released
278 1.0, // adjusted on 0.7.6
279 0.10,
280 0.20,
281 0.10,
282 0.50,
283 5.0,
284 0.0,
285 0.0,
286 0.0,
287 0.9,
288 1.0,
289 0.5,
290 0.5
291 ];
292 }else{
293 gTSvarList = [ 0.50, // how fast to reach max speed
294 10, // how fast to reach min speed or zero
295 <1.0,1.0,1.0>, // XYZ linear friction
296 <1.0,1000.0,1000.0>, // XYZ angular friction
297 0.20, // how fast turning force is applied
298 0.1, // how fast turning force is released
299 0.1, // adjusted on 0.7.6
300 10.0,
301 0.10,
302 10.0,
303 1.0,
304 2.0,
305 0.0,
306 0.0,
307 0.0,
308 0.99,
309 1.0,
310 0.5,
311 0.5
312 ];
313 }
314 gVLMT=llList2Float(gTSvarList,0);
315 gVLMDT=llList2Float(gTSvarList,1);
316 gVLFT=llList2Vector(gTSvarList,2);
317 gVAFT=llList2Vector(gTSvarList,3);
318 gVAMT=llList2Float(gTSvarList,4);
319 gVAMDT=llList2Float(gTSvarList,5);
320 gVLDE=llList2Float(gTSvarList,6);
321 gVLDT=llList2Float(gTSvarList,7);
322 gVADE=llList2Float(gTSvarList,8);
323 gVADT=llList2Float(gTSvarList,9);
324 gVVAE=llList2Float(gTSvarList,10);
325 gVVAT=llList2Float(gTSvarList,11);
326 gVHE=llList2Float(gTSvarList,12);
327 gVHT=llList2Float(gTSvarList,13);
328 gVHH=llList2Float(gTSvarList,14);
329 gVB=llList2Float(gTSvarList,15);
330 gVBE=llList2Float(gTSvarList,16);
331 gVBM=llList2Float(gTSvarList,17);
332 gVBT=llList2Float(gTSvarList,18);
333 }
334
335 init_PhysEng(){
336 string msg;
337
338
339 if(gPhysEng==0){
340
341 init_TSvar(0);
342 gTurnRatioList = [ 1.4, // HOVER
343 2.4, // 15-KNOTS
344 2.4, // 30-KNOTS
345 2.5, // 40-KNOTS
346 4.5, // 50-KNOTS
347 5.0, // 60-KNOTS
348 5.5, // 70-KNOTS
349 6.5, // 100-KNOTS
350 7.5, // 150-KNOTS
351 10.0, // 200-KNOTS
352 10.0, // 225-KNOTS
353 10.0 // 250-KNOTS
354 ];
355 gGearPowerList = [ 5, // HOVER
356 15, // 15-KNOTS
357 30, // 30-KNOTS
358 40, // 40-KNOTS
359 50, // 50-KNOTS
360 60, // 60-KNOTS
361 70, // 70-KNOTS
362 100, // 100-KNOTS
363 150, // 150-KNOTS
364 200, // 200-KNOTS
365 225, // 225-KNOTS
366 250 // 250-KNOTS
367 ];
368
369
370 }else{
371 gTurnMulti=1;
372 init_TSvar(1);
373 gTurnRatioList = [ 1.3, // HOVER
374 1.2, // 15-KNOTS
375 1.4, // 30-KNOTS
376 2.0, // 40-KNOTS
377 2.9, // 50-KNOTS
378 3.3, // 60-KNOTS
379 5.0, // 70-KNOTS
380 6.0, // 100-KNOTS
381 7.0, // 150-KNOTS
382 8.0, // 200-KNOTS
383 9.0, // 225-KNOTS
384 10.0 // 250-KNOTS
385 ];
386
387 gGearPowerList = [ 5, // HOVER
388 15, // 15-KNOTS
389 30, // 30-KNOTS
390 40, // 40-KNOTS
391 50, // 50-KNOTS
392 60, // 60-KNOTS
393 70, // 70-KNOTS
394 100, // 100-KNOTS
395 150, // 150-KNOTS
396 200, // 200-KNOTS
397 225, // 225-KNOTS
398 250 // 250-KNOTS
399 ];
400
401 }
402
403 }
404
405
406
407 init_engine(){
408 gRun = FALSE;
409 llSetSitText(gSitMessage);
410 llCollisionSound("", 0.0);
411
412
413
414 llSetLinkPrimitiveParamsFast(LINK_ALL_CHILDREN, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_NONE]);
415
416 }
417
418 init_followCam(){
419
420 powershift(0);
422 CAMERA_ACTIVE, 1, // 0=INACTIVE 1=ACTIVE
423 CAMERA_BEHINDNESS_ANGLE, 15.0, // (0 to 180) DEGREES
424 CAMERA_BEHINDNESS_LAG, 1.0, // (0 to 3) SECONDS
425 CAMERA_DISTANCE, 30.0, // ( 0.5 to 10) METERS
426 PITCH" title="View Definition" class="tooltip">CAMERA_PITCH, 6.0, // (-45 to 80) DEGREES
427 CAMERA_POSITION_LOCKED, FALSE, // (TRUE or FALSE)
428 CAMERA_POSITION_LAG, 0.01, // (0 to 3) SECONDS
429 CAMERA_POSITION_THRESHOLD, 30.0, // (0 to 4) METERS
430 CAMERA_FOCUS_LOCKED, FALSE, // (TRUE or FALSE)
431 CAMERA_FOCUS_LAG, 0.01 , // (0 to 3) SECONDS
432 CAMERA_FOCUS_THRESHOLD, 0.01, // (0 to 4) METERS
433 CAMERA_FOCUS_OFFSET, <0.0,0.0,0.0> // <-10,-10,-10> to <10,10,10> METERS
434 ]);
435 }
436
437 init_fixedCam(float degrees) {
438 rotation sitRot = llAxisAngle2Rot(<0, 0, 1>, degrees * PI);
439 llSetCameraEyeOffset(<-20.0, 0, 8> * sitRot);
440 llSetCameraAtOffset(<4, 0, 0> * sitRot);
442 }
443
444 set_engine(){
449 llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME, <0.00000, 0.00000, 0.00000, 0.00000>);
450 llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, gVLMT);
455 llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, gVAMDT);
469 }
470
471 powershift(integer g){
472 if(gRun & ! gMoving){
473 if(gCamFixed==0){
475 }
476 } else {
477 vector vel = llGetVel();
478 float speed = llVecMag(vel);
479 if(speed <=20){
480 if(gCamFixed==0){
482 }
483 }
484 else if((speed >=21) || (speed <=50)){
485 if(gCamFixed==0){
487 }
488 }
489 else if(speed >=51) {
490 if(gCamFixed==0){
492 }
493 }
494 }
495 gGearPower = llList2Integer(gGearPowerList, g);
496 }
497
498 gearshift(integer g){
499 gGearName = llList2String(gGearNameList, g);
500 flightSound();
501 llSay(0,gGearName);
502 }
503
504 nearestpi(){
505 // ALIGNS THE VEHICLE EAST WEST SOUTH NORTH BASED ON CURRENT ROTATION (PRESTAGE AND STAGE DURING DRAG MODE)
507 llSetRot( llEuler2Rot( <Rad.x, Rad.y, llRound( Rad.z / PI_BY_TWO ) * PI_BY_TWO > ) );
508 }
509
510 menu(key user,string title,list buttons)
511 {
512 llListenRemove(menu_handler);
513 menu_channel = (integer)(llFrand(99999.0) * -1);
514 menu_handler = llListen(menu_channel,"","","");
515 llDialog(user,title,buttons,menu_channel);
516 llSetTimerEvent(30.0);
517 }
518
519 showdata(string s){
520 llSetText(s,<1,1,.6>,1.0);
521 }
522
523 flightSound(){
524 vector vel = llGetVel();
525 float speed = llVecMag(vel);
526 if(speed <=15){
527 //gTurnMulti=1.012345;
528 gNewSound = gSoundLoopIdle;
529 } else if(speed >=30){
530 // gTurnMulti=2.012345;
531 gNewSound = gSoundLoopSlow;
532 } else {
533 // gTurnMulti=3.012345;
534 gNewSound = gSoundLoopAggressive;
535 }
536
537 if(gOldSound != gNewSound){
538 llLoopSound(gNewSound,1.0);
539 gOldSound = gNewSound;
540 }
541 }
542
543 default {
544
545 on_rez(integer param) {
547 }
548
550 llListen(DragonChannel,"","","");
551 rotation SitRot = llEuler2Rot(DEG_TO_RAD * gSitRot);
552 llSitTarget(gSitPos, SitRot);
554 init_engine();
555 }
556
557 changed(integer change){
558 if(change & CHANGED_LINK) {
559 dragonState = "sitting";
560 key kWhoSat= llAvatarOnSitTarget();
561 if(osIsNpc(kWhoSat)){
562 osNpcStopAnimation(kWhoSat,"sit"); // fkb
563 NPCAnimate(gDragonSitAnim1 ,gDragonSitAnim1Timeout);
564 llSay(0,"Ready!");
565 }
566 } else {
568 gRun = FALSE;
569 init_engine();
570 llTriggerSound(gSoundStop,1);
571 llStopAnimation(gDrivingAnim);
572 llPushObject(gAgent, <0,3,20>, ZERO_VECTOR, FALSE);
573 llSetTimerEvent(0.0);
577 llSetCameraParams([CAMERA_ACTIVE, 0]);
578 llSetText("",<0,0,0>,1.0);
579
580 }
581 }
582
583 link_message(integer sender_number, integer number, string message, key id)
584 {
585 DEBUG("Link Msg : " + message);
586 if(message == "Seated")
587 {
588 gAgent = id;
589
591 llSetStatus(STATUS_ROTATE_Y,TRUE);
592
593 init_PhysEng();
594
595
596 set_engine();
597 DEBUG("Perms request");
598
599 DEBUG("Agent: " + gAgent);
601
602
603
604 } else if(message == "Unseated") {
605
606 SafeMode(); // shut down physics
609
610 DEBUG("NPC Key to delete = :" + (string) gNPCKey);
611
612 if(gNPCKey != NULL_KEY)
613 osNpcRemove(gNPCKey);
614
615 llSetTimerEvent(0.0);
616 llSetText("",<0,0,0>,1.0);
617
618 if(gInProduction)
619 llDie();
620
621 } else if(message == "NPCkey") {
622
623 gNPCKey = id;
624 DEBUG("NPC Key located:" + (string) gNPCKey);
625 }
626
627 }
628
630
631 DEBUG("permissions = " + (string) perm);
632
634 DEBUG("Animation granted");
635 llStopAnimation("sit");
636 AvatarAnimate(gAvatarSitAnim);
637 }
638
640 DEBUG("Controls granted");
641 gGear = 0; // LIST INDEX STARTS @ 0
643 }
644
646 DEBUG("Camera granted");
647 if(gCamFixed == 1) {
648 init_fixedCam(0);
649 } else {
650 init_followCam();
651 }
652 }
653 gRun = TRUE;
654 llTriggerSound(gSoundStartup,1.0);
655 flightSound();
656 llShout(3454,"rez");
657
658 llSay(0,"Page UP and Page Down Keys go up and down. Up and Down Arrow Keys move forward or backward and Left and Right Arrow Keys turn.");
659 llSay(0,"Shift + Right Arrow Key to speed up and Shift + Left Arrow Key to slow down.");
660
661 llSay(0,"Almost ready... please wait for your dragon to arrive.");
662
663 }
664
665
666
667 control(key id, integer held, integer change){
668 if(! gRun)
669 return;
670
671 vector vel = llGetVel();
672 vector speedvec = llGetVel() / llGetRot();
673 gSpeed = llVecMag(vel);
674 vector linear;
675
676
677 if(gSpeed > 3) {
678 if(dragonState != "flyingfast")
679 dragonState = "flyingfast";
680 NPCAnimate(gDragonFlyFastAnim,gDragonFlyFastAnimTimeout);
681 } else if(gSpeed > .1) {
682 if(dragonState != "flyingslow")
683 dragonState = "flyingslow";
684 NPCAnimate(gDragonFlySlowAnim,gDragonFlySlowAnimTimeout);
685 }
686
687 gTurnRatio = llList2Float(gTurnRatioList,gGear);
688
689 if((held & change & CONTROL_RIGHT) || ((gGear >= 11) && (held & CONTROL_RIGHT))){
690 gGear=gGear+1;
691 if(gGear < 0) gGear = 0;
692 if(gGear > 11) gGear = 11;
693 gearshift(gGear);
694 }
695
696 if((held & change & CONTROL_LEFT) || ((gGear >= 11) && (held & CONTROL_LEFT))){
697 gGear=gGear-1;
698 if(gGear < 0) gGear = 0;
699 if(gGear > 11) gGear = 11;
700 gearshift(gGear);
701
702 }
703
704 if(held & CONTROL_FWD){
705
706 powershift(gGear);
708 gMoving=1;
709
710 }
711
712 if(held & CONTROL_BACK){
716 gTurnRatio = -2.0;
717 }
718 if(~held & change & CONTROL_FWD){
719 if(gGear > 9){
720 gGear = 3;
721 }
722 }
723
724
725 showdata(llGetSubString((string)(gSpeed*2.23692912),0,2) + " mph");
726
727
728 vector AngularMotor;
729 AngularMotor.y = 0;
730 if(held & (CONTROL_ROT_RIGHT)){
731 if(gGear<3){
732 AngularMotor.x += ((gTurnRatio/gTurnMulti)*1);
733 AngularMotor.z -= ((gTurnRatio*gTurnMulti)/1);
734 }else if(gGear==3){
735 AngularMotor.x += ((gTurnRatio/gTurnMulti)*1.5);
736 AngularMotor.z -= ((gTurnRatio/gTurnMulti)/2);
737 }else{
738 AngularMotor.x += ((gTurnRatio/gTurnMulti)*2);
739 AngularMotor.z -= ((gTurnRatio/gTurnMulti)/8);
740 }
741
742 }
743
744 if(held & (CONTROL_ROT_LEFT)){
745 if(gGear<3){
746 AngularMotor.x -= ((gTurnRatio/gTurnMulti)*1);
747 AngularMotor.z += ((gTurnRatio*gTurnMulti)/1);
748 }else if(gGear==3){
749 AngularMotor.x -= ((gTurnRatio/gTurnMulti)*1);
750 AngularMotor.z += ((gTurnRatio/gTurnMulti)/2);
751 }else{
752 AngularMotor.x -= ((gTurnRatio/gTurnMulti)*1);
753 AngularMotor.z += ((gTurnRatio/gTurnMulti)/8);
754 }
755
756
757 }
758 // going up or stop going up
759 if(held & CONTROL_UP) {
760 if(gGear!=0){
761 linear = <gGearPower,0,gVerticalThrust*gGear>;
763 AngularMotor.y -= 0.5; //((gTurnRatio/gTurnMulti)*1);
764 }else{
765 linear =<0,0,gVerticalThrust>;
767 }
768 } else if(change & CONTROL_UP) {
769 if(gGear!=0){
770 linear = <gGearPower,0,0>;
772 AngularMotor.y = 0;
773 }else{
774 linear = <0,0,0>;
776 }
777 }
778
779
780
781 // going down or stop going down
782
783 if(held & CONTROL_DOWN) {
784 if(gGear!=0){
785 linear = <gGearPower,0,-gVerticalThrust*gGear>;
787 AngularMotor.y += 0.5; //((gTurnRatio/gTurnMulti)*1);
788 }else{
789 linear = <0,0,-gVerticalThrust>;
791 }
792 } else if(change & CONTROL_DOWN) {
793 if(gGear!=0){
794 linear = <gGearPower,0,0>;
796 AngularMotor.y = 0;
797 }else{
798 linear = <0,0,0>;
800 }
801 }
803
804 if(gDebug)
805 {
806 showdata((string) AngularMotor + ":" + (string) linear);
807 }
808 }
809 touch_start(integer total_number){
810 if(gAgent != NULL_KEY){
811 menu(llDetectedKey(0), "\nDriver's Menu.", MENU_MAIN);
812 }
813 }
814
815 listen(integer channel,string name,key id,string message){
816 DEBUG(message);
817 if(channel == DragonChannel && (key) message == llGetKey())
818 {
819
820 if(gAgent != NULL_KEY)
821 llSay(DragonChannelBack, (string) gAgent);
822 return;
823 }
824
825
826 llListenRemove(menu_handler);
828
829 if(message == "Align"){
830 llSay(0, "All lined up. Go!");
831 nearestpi();
832 }
833 else if(message == "Steam"){
834 llMessageLinked(LINK_SET,1, "steam","");
835 Steam();
836 }
837 else if(message == "Flame"){
838 llMessageLinked(LINK_SET,1, "flame","");
839 Flame();
840 }
841 else if(message == "Growl"){
842 llMessageLinked(LINK_SET,1, "flame","");
843 Growl();
844 }
845 else if(message == "Help"){
846 llWhisper(0,"Hover Mode: Page UP and Page Down Keys controls elevation, Up and Down Arrow Keys to move forward or backward and finally Left and Right Arrow Keys to spin left or right.");
847 llWhisper(0,"Fly Mode: UP and Page Down Keys controls elevation, Up and Down Arrow Keys to move forward or backward and finally Left and Right Arrow Keys to roll left or right.");
848 llWhisper(0,"Speed: Shift + Right Arrow Key to speed up and Shift + Left Arrow Key to slow down.");
849 }
850 else if(message == "Remove NPC"){
852 }
853 }
854
855 timer(){
856 llListenRemove(menu_handler);
857 llSetTimerEvent(0.0);
858
859 if(dragonState == "flyingslow")
860 {
861 NPCAnimate(gDragonFlySlowAnim,gDragonFlySlowAnimTimeout);
862
863 } else if(dragonState == "flyingfast") {
864 NPCAnimate(gDragonFlyFastAnim,gDragonFlyFastAnimTimeout );
865 } else if(dragonState == "sitting"){
866 float rand = llFrand(5);
867 if(rand < 2.5)
868 NPCAnimate(gDragonSitAnim1,gDragonSitAnim1Timeout);
869 else
870 NPCAnimate(gDragonSitAnim2,gDragonSitAnim2Timeout);
871 }
872 }
873 }

Opensim Ridable NPC

Rideable NPC dragon, (and other flying creatures) Original script by Shin Ingen

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

Opensim Ridable NPC

Commands for the notecard to make the dragon or bird appeear

Category: Flying NPC's
By : Ferd Frederix
Created: 2015-03-17 Edited: 2015-04-14
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=Dragon Rider|<0.000000,0.000000,0.500000>
2 @sound=DragonRoar
3 @cmd=1|wingsin
4 @cmd=1|flameon
5 @walk=<0.000000,10,0.00000>
6 @cmd=1|wingsout
7 @fly=<10.0,0,0>
8 @wander=20|2
9 @fly=<0.000000,0,1.0>
10 @sit=Dragon Rider
11 @rotate=90
12 @sound=monster-short-roar
13 @cmd=1|wingsout
14 @animate=DragonFlame|4
15 @cmd=1|wingsin
16 @stop

Opensim Ridable NPC

PoseBall for back of dragon

Category: Flying NPC's
By : Ferd Frederix
Created: 2015-03-17 Edited: 2015-04-14
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 // Rev 0.1 2014-12-08 Compiles
3
4 integer gDebug = TRUE; // set to TRUE or FALSE for debug chat on various actions
5
6 DEBUG(string str)
7 {
8 if(gDebug)
9 llOwnerSay(llGetScriptName() + ":" + str); // Send the owner debug info so you can chase NPCS
10 }
11
12
13 default
14 {
16 {
17 llSitTarget(<0,0,0.1>,ZERO_ROTATION);
18 llSay(0, "Hello, sit on the dragon pose ball to fly!");
19 llSetSitText("Fly");
21 }
22
24 {
26 }
27
28 changed(integer change)
29 {
30 if(change & CHANGED_LINK)
31 {
32 key agent = llAvatarOnSitTarget();
33 if(agent != NULL_KEY)
34 {
35 DEBUG("Seated");
36 llMessageLinked(LINK_SET,0,"Seated",agent);
38 }
39 else
40 {
42 llMessageLinked(LINK_SET,0,"Unseated","");
43 }
44 }
45 }
46 }

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