From 28f9f0fb652e1c763207439123675c2aa0588f73 Mon Sep 17 00:00:00 2001 From: Fred Beckhusen Date: Tue, 14 Apr 2020 16:20:55 -0500 Subject: [PATCH] Add new files --- FlashLamp/FlashLamp.sol | 3 + FlashLamp/FlashLamp/FlashLamp.prj | 6 + FlashLamp/FlashLamp/Object/Flash.lsl | 39 + FlashLamp/Object/Flash.lsl | 37 - GameOfFireAndIce | 1 - .../Object/Open Source Lag meter.lsl | 133 +- .../NPC Chatbot for Opensim.prj | 34 - .../NPC Chatbot for Opensim/Object/Appearance | 16 +- .../Object/Chatbot Controller.lsl | 40 +- .../Object/NPC Control Script.lsl | 36 +- .../NPC Chatbot for Opensim/Object/Path | 14 +- .../Train/Opensim Train/Object/NoteCard.txt | 9 + .../Opensim Train/Object/Opensim Train.lsl | 1518 +++++++++++++++++ .../Train/Opensim Train/Opensim Train.prj | 8 + Opensim Train/Train/Train.sol | 3 + .../Opensim VIsitors List/Object/Script.lsl | 189 +- .../Paramour Traffic Monitor.sol | 3 + .../Object/Notecard.txt | 60 + .../Paramour+Region+Traffic+Monitor+v2.0.lsl | 348 ++++ ...r+Region+Traffic+Monitor+2.0 Script 2.lsl | 247 +++ .../Paramour Traffic Monitor.prj | 10 + .../Trash Collector NPC/NPC Box/Trash Bot | 1 + .../NPC Box/Trash Collector script.lsl | 262 +-- .../Trash Collector NPC.prj | 2 + 24 files changed, 2603 insertions(+), 416 deletions(-) create mode 100644 FlashLamp/FlashLamp.sol create mode 100644 FlashLamp/FlashLamp/FlashLamp.prj create mode 100644 FlashLamp/FlashLamp/Object/Flash.lsl delete mode 100644 FlashLamp/Object/Flash.lsl delete mode 160000 GameOfFireAndIce create mode 100644 Opensim Train/Train/Opensim Train/Object/NoteCard.txt create mode 100644 Opensim Train/Train/Opensim Train/Object/Opensim Train.lsl create mode 100644 Opensim Train/Train/Opensim Train/Opensim Train.prj create mode 100644 Opensim Train/Train/Train.sol create mode 100644 Paramour Traffic Monitor/Paramour Traffic Monitor.sol create mode 100644 Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Notecard.txt create mode 100644 Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Paramour+Region+Traffic+Monitor+v2.0.lsl create mode 100644 Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Paramour+Stats+Retriever+for+Region+Traffic+Monitor+2.0 Script 2.lsl create mode 100644 Paramour Traffic Monitor/Paramour Traffic Monitor/Paramour Traffic Monitor.prj create mode 100644 Trash Collector NPC/Trash Collector NPC/NPC Box/Trash Bot diff --git a/FlashLamp/FlashLamp.sol b/FlashLamp/FlashLamp.sol new file mode 100644 index 00000000..d2752a49 --- /dev/null +++ b/FlashLamp/FlashLamp.sol @@ -0,0 +1,3 @@ + + + diff --git a/FlashLamp/FlashLamp/FlashLamp.prj b/FlashLamp/FlashLamp/FlashLamp.prj new file mode 100644 index 00000000..2a14bb91 --- /dev/null +++ b/FlashLamp/FlashLamp/FlashLamp.prj @@ -0,0 +1,6 @@ + + + + + diff --git a/FlashLamp/FlashLamp/Object/Flash.lsl b/FlashLamp/FlashLamp/Object/Flash.lsl new file mode 100644 index 00000000..5fe37539 --- /dev/null +++ b/FlashLamp/FlashLamp/Object/Flash.lsl @@ -0,0 +1,39 @@ +// :SHOW: +// :CATEGORY:Light +// :NAME:Flashing Light +// :AUTHOR:Unknown +// :KEYWORDS:Light +// :REV:1 +// :WORLD:Opensim, Second Life +// :DESCRIPTION: +// Blinks a light +// :CODE: + + +integer root = 0 ; // change to the number of a prim that you want to flash +float timeval = 0.1; //the interval between events, smaller = faster +integer counter = 0; + +On() { + llSetLinkPrimitiveParamsFast(root,[PRIM_POINT_LIGHT, TRUE, <1,1,1>, 1.0, 10, .1, PRIM_FULLBRIGHT, ALL_SIDES,TRUE, PRIM_GLOW, ALL_SIDES, 1.0]); +} +Off() { + llSetLinkPrimitiveParamsFast(root,[PRIM_POINT_LIGHT, FALSE, <1,1,1>, 1.0, 10, .1, PRIM_FULLBRIGHT, ALL_SIDES,FALSE, PRIM_GLOW, ALL_SIDES, 0.0]); +} +default +{ + state_entry() + { + Off(); + + llSetTimerEvent(timeval); // between events + } + timer() + { + if (counter %2 == 0) + On(); + else + Off(); + counter++; + } +} \ No newline at end of file diff --git a/FlashLamp/Object/Flash.lsl b/FlashLamp/Object/Flash.lsl deleted file mode 100644 index dec8e92f..00000000 --- a/FlashLamp/Object/Flash.lsl +++ /dev/null @@ -1,37 +0,0 @@ - -integer root = 0 ; // change to the number of a prim that you want to flash -float timeval = 0.1; //the interval between events, smaller = faster -integer counter = 0; - -On() { - llSetLinkPrimitiveParamsFast(root,[PRIM_POINT_LIGHT, TRUE, <1,1,1>, 1.0, 10, .1, PRIM_FULLBRIGHT, ALL_SIDES,TRUE, PRIM_GLOW, ALL_SIDES, 1.0]); -} -Off() { - llSetLinkPrimitiveParamsFast(root,[PRIM_POINT_LIGHT, FALSE, <1,1,1>, 1.0, 10, .1, PRIM_FULLBRIGHT, ALL_SIDES,FALSE, PRIM_GLOW, ALL_SIDES, 0.0]); -} -default -{ - state_entry() - { - Off(); - } - touch_start(integer total_number) - { - llSetTimerEvent(timeval); // between events - } - timer() - { - if (counter == 0) - On(); - else if (counter == 1) - Off(); - else if (counter == 2) - On(); - else if (counter >= 3) { - Off(); - counter = -1; - llSetTimerEvent(0); - } - counter++; - } -} \ No newline at end of file diff --git a/GameOfFireAndIce b/GameOfFireAndIce deleted file mode 160000 index d446b505..00000000 --- a/GameOfFireAndIce +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d446b50561b080dff4666a0c70263808992ec7cb diff --git a/Lag Meter/Lag Meter/Object/Open Source Lag meter.lsl b/Lag Meter/Lag Meter/Object/Open Source Lag meter.lsl index 5e3fdcc7..885265d1 100644 --- a/Lag Meter/Lag Meter/Object/Open Source Lag meter.lsl +++ b/Lag Meter/Lag Meter/Object/Open Source Lag meter.lsl @@ -1,8 +1,15 @@ -//:AUTHOR: Chaser.Zaks - +// :SHOW: +// :CATEGORY:Meter +// :NAME:Lag Meter +// :AUTHOR:Chaser.Zaks +// :KEYWORDS:Lag Meter +// :REV:1 +// :WORLD:Opensim +// :DESCRIPTION: +// Lag Meter +// :CODE: //Link two boxes then put the script inside. -//By Chaser.Zaks. //Feel free to redistribute and use in projects(even in sold products, just keep it open source). //DO NOT CLOSE SOURCE OR SELL ALONE. @@ -23,65 +30,65 @@ list same_params =[PRIM_SLICE, <0.5, 1.0, 0.0>,PRIM_FULLBRIGHT,ALL_SIDES,TRUE,PR default { - state_entry() - { + state_entry() + { - llSetObjectName("Open Source Lag Meter v3"); - llSetLinkPrimitiveParams(1,same_params+[PRIM_COLOR,ALL_SIDES,<0,0,0>,.4,PRIM_SIZE,<.5,.5,4>]); - llSetLinkPrimitiveParams(2,same_params+[PRIM_COLOR,ALL_SIDES,<0,1,0>,1.,PRIM_SIZE,<.4,.4,3.8>,PRIM_POS_LOCAL,<0,0,.04>]); - llSetText("Initalizing...",<1,1,0>, 1.0); - //First start, Set some stuff. - lastrestart = llGetUnixTime(); - llSetTimerEvent(0.5); //One second is too much checking. Let's not be a resource hog. - } - changed(integer change) - { - if(change & CHANGED_REGION_START) - lastrestart = llGetUnixTime(); - } - timer(){ - region = llGetRegionName(); - avatars = llGetListLength(llGetAgentList(AGENT_LIST_REGION, [])); - //Restart time - lastrestartedcalc = llGetUnixTime()-lastrestart; - days=0; - hours=0; - minutes=0; - seconds=0; - do{ - if(lastrestartedcalc>=86399){ - days++; - lastrestartedcalc=lastrestartedcalc-86399; - }else if(lastrestartedcalc>=3599){ - hours++; - lastrestartedcalc=lastrestartedcalc-3599; - }else if(lastrestartedcalc>=59){ - minutes++; - lastrestartedcalc=lastrestartedcalc-59; - }else{ - seconds++; - lastrestartedcalc--; - } - }while(lastrestartedcalc>=0); - float region_time_dilation=llGetRegionTimeDilation(); - if(region_time_dilation>=0.75) - color=<0,1,0>; - else if(region_time_dilation>=0.50) - color=<1,1,0>; - else - color=<1,0,0>; - integer primsused=llGetParcelPrimCount(llGetPos(), PARCEL_COUNT_TOTAL, MeasureNonParcelPrims); - integer maxprims=llGetParcelMaxPrims(llGetPos(), MeasureNonParcelPrims); - llSetText( - "Region: "+region+ - "\nAvatars: "+(string)avatars+ - "\nPrims left: "+(string)(maxprims-primsused)+" ("+(string)primsused+"/"+(string)maxprims+")"+ - "\nDilation: "+llGetSubString((string)((1.-region_time_dilation)*100.), 0, 3)+"%"+ - "\nFPS: "+llGetSubString((string)llGetRegionFPS(), 0, 5)+ - "\nLast restart:\n"+(string)days+" Days, "+(string)hours+" Hours, "+(string)minutes+" Minutes, and "+(string)seconds+" Seconds ago.", - //"\nLast restart:\n"+(string)days+":"+(string)hours+":"+(string)minutes+":"+(string)seconds, - color, 1.0); - //llSetLinkPrimitiveParamsFast(2,[PRIM_SLICE,<0,(region_time_dilation),0>,PRIM_COLOR,ALL_SIDES,color,1]); - llSetLinkPrimitiveParamsFast(2,[PRIM_SLICE, <0.5, 1., 0.0>,PRIM_SIZE,<0.4, 0.4, 3.80*region_time_dilation>,PRIM_COLOR,ALL_SIDES,color,1]); - } + llSetObjectName("Open Source Lag Meter v3"); + llSetLinkPrimitiveParams(1,same_params+[PRIM_COLOR,ALL_SIDES,<0,0,0>,.4,PRIM_SIZE,<.5,.5,4>]); + llSetLinkPrimitiveParams(2,same_params+[PRIM_COLOR,ALL_SIDES,<0,1,0>,1.,PRIM_SIZE,<.4,.4,3.8>,PRIM_POS_LOCAL,<0,0,.04>]); + llSetText("Initalizing...",<1,1,0>, 1.0); + //First start, Set some stuff. + lastrestart = llGetUnixTime(); + llSetTimerEvent(0.5); //One second is too much checking. Let's not be a resource hog. + } + changed(integer change) + { + if(change & CHANGED_REGION_START) + lastrestart = llGetUnixTime(); + } + timer(){ + region = llGetRegionName(); + avatars = llGetListLength(llGetAgentList(AGENT_LIST_REGION, [])); + //Restart time + lastrestartedcalc = llGetUnixTime()-lastrestart; + days=0; + hours=0; + minutes=0; + seconds=0; + do{ + if(lastrestartedcalc>=86399){ + days++; + lastrestartedcalc=lastrestartedcalc-86399; + }else if(lastrestartedcalc>=3599){ + hours++; + lastrestartedcalc=lastrestartedcalc-3599; + }else if(lastrestartedcalc>=59){ + minutes++; + lastrestartedcalc=lastrestartedcalc-59; + }else{ + seconds++; + lastrestartedcalc--; + } + }while(lastrestartedcalc>=0); + float region_time_dilation=llGetRegionTimeDilation(); + if(region_time_dilation>=0.75) + color=<0,1,0>; + else if(region_time_dilation>=0.50) + color=<1,1,0>; + else + color=<1,0,0>; + integer primsused=llGetParcelPrimCount(llGetPos(), PARCEL_COUNT_TOTAL, MeasureNonParcelPrims); + integer maxprims=llGetParcelMaxPrims(llGetPos(), MeasureNonParcelPrims); + llSetText( + "Region: "+region+ + "\nAvatars: "+(string)avatars+ + "\nPrims left: "+(string)(maxprims-primsused)+" ("+(string)primsused+"/"+(string)maxprims+")"+ + "\nDilation: "+llGetSubString((string)((1.-region_time_dilation)*100.), 0, 3)+"%"+ + "\nFPS: "+llGetSubString((string)llGetRegionFPS(), 0, 5)+ + "\nLast restart:\n"+(string)days+" Days, "+(string)hours+" Hours, "+(string)minutes+" Minutes, and "+(string)seconds+" Seconds ago.", + //"\nLast restart:\n"+(string)days+":"+(string)hours+":"+(string)minutes+":"+(string)seconds, + color, 1.0); + //llSetLinkPrimitiveParamsFast(2,[PRIM_SLICE,<0,(region_time_dilation),0>,PRIM_COLOR,ALL_SIDES,color,1]); + llSetLinkPrimitiveParamsFast(2,[PRIM_SLICE, <0.5, 1., 0.0>,PRIM_SIZE,<0.4, 0.4, 3.80*region_time_dilation>,PRIM_COLOR,ALL_SIDES,color,1]); + } } \ No newline at end of file diff --git a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/NPC Chatbot for Opensim.prj b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/NPC Chatbot for Opensim.prj index f0cd5da6..d5893566 100644 --- a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/NPC Chatbot for Opensim.prj +++ b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/NPC Chatbot for Opensim.prj @@ -2,45 +2,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Appearance b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Appearance index 4a30e185..c62302ad 100644 --- a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Appearance +++ b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Appearance @@ -1,15 +1 @@ -// :SHOW: -// :CATEGORY:ChatBot -// :NAME:NPC Chatbot for Opensim -// :AUTHOR:Ferd Frederix -// :KEYWORDS: -// :CREATED:2016-07-27 16:13:54 -// :EDITED:2016-07-27 15:13:54 -// :ID:1108 -// :NUM:1905 -// :REV:1.0 -// :WORLD:OpenSim -// :DESCRIPTION: -// Sample Apperance notecard -// :CODE: -Appearance notecard will be made here +Appearance notecard goes here \ No newline at end of file diff --git a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Chatbot Controller.lsl b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Chatbot Controller.lsl index 038833aa..ed1a718a 100644 --- a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Chatbot Controller.lsl +++ b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Chatbot Controller.lsl @@ -1,40 +1,31 @@ -// :SHOW: // :CATEGORY:ChatBot -// :NAME:NPC Chatbot for Opensim -// :AUTHOR:Fred Beckhusen (Ferd Frederix) -// :KEYWORDS: -// :CREATED:2016-07-27 16:13:55 -// :EDITED:2016-07-27 15:13:55 -// :ID:1108 -// :NUM:1906 +// :NAME:PersonalityForge Chatbot +// :AUTHOR:Ferd Frederix // :REV:1.0 -// :WORLD:OpenSim +// :WORLD:Second Life, OpenSim // :DESCRIPTION: -// This chatbot is for OpenSim Only. It only works on NPC's with a modified All-In-One NPC script, which is also included +// This chatbot is for OpenSim Only. It only works on NPC's. + +// Chatbot for PersonalityForge. Get a free account at http://www.personalityforge.com. // 5,000 chats are free. // :CODE: -// Chatbot uses PersonalityForge. Get a free account at http://www.personalityforge.com. -// first, get a free account at http://www.personalityforge.com. +// fiorst, get a free account at http://www.personalityforge.com. // Get an API ID, and add it to the apiKey : -string apiKey = "BlahBlah"; // your supplied apiKey from your Chat Bot API subscription - - -// Add the domain or IP of your OpenSim server to the list of authorized domains at http://www.personalityforge.com/botland/myapi.php -// Add a checkmark to the "Enable Simple API" in your account. +string apiKey = "keU6Hv3tbk318wn1"; // your supplied apiKey from your Chat Bot API subscription + +// Add the domain *.secondlife.com or your OpenSim server IP to the list of authorized domains at http://www.personalityforge.com/botland/myapi.php +// Add a checkmark to the "Enable Simple API" in tyour account. // Click on the Simple API tab and pick a chatbot ID from the list of chatbots under the heading "Selecting A Chat Bot ID" // for example, Countess Elvira is 99232. Put that in chatBot ID below. // Sex Bot Ciran is 100387. // 754 is Liddora a sexy tart string chatBotID = "23958"; // the ID of the chat bot you're talking to - -// The first time, you may want to test it with this set to TRUE -integer debug = FALSE; // Set this TRUE to see the gory details - -// more constants below -integer greeting = TRUE; // if TRUE, say hello when anyone comes up. +integer greeting = TRUE; // if TRUE, say hello when anyone comes up. + integer lookat = FALSE; +integer debug = FALSE; // Set this TRUE to see the gory details ////////// REMOVE THESE IN WORLD //////////////// ///////// LSLEDIT DEBUG ONLY//////////////////// @@ -45,7 +36,7 @@ integer lookat = FALSE; ///////////////////////////////////////////////////////////////////// // various tuneable code bits -float range = 35; // haw far away an avatar is before we greet them/. No point in making this more than 20, that cannot hear us after that +float range = 35; // haw far awy an avatar is before we greet them/. No point in making this more than 20, that cannot hear us after that float wpm = 85; // 33 wpm = 2.75 cps @ 5 chars per word for a typical avatar to type with. // Larger numbers make your NPC answer quicker. float cps; @@ -104,6 +95,7 @@ string KeyValueGet(string var) { default { + on_rez(integer param) { llResetScript(); diff --git a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/NPC Control Script.lsl b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/NPC Control Script.lsl index 9f58ca6c..bc5abd6b 100644 --- a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/NPC Control Script.lsl +++ b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/NPC Control Script.lsl @@ -1,24 +1,34 @@ -// :SHOW: -// :CATEGORY:ChatBot -// :NAME:NPC Chatbot for Opensim -// :AUTHOR:Fred Beckhusen (Ferd Frederix) +// :CATEGORY:NPC +// :NAME:All_in_1_NPC_Recorder_and_Player +// :AUTHOR:Ferd Frederix // :KEYWORDS: -// :CREATED:2016-07-27 16:14:00 -// :EDITED:2016-07-27 15:14:00 -// :ID:1108 -// :NUM:1907 -// :REV:1.0 +// :CREATED:2013-09-08 18:27:47 +// :ID:27 +// :NUM:1612 +// :REV:1.6 // :WORLD:OpenSim // :DESCRIPTION: -// Modified All in one NPC recorder player for NPC use +// All in one NPC recorder player. +// Supports both absolute and relative paths and many new commands +// Add animations named "Fly, Walk, Stand and Run" // Click Prim to use. - +// Should be worn as a HUD to record. +// Put it on the ground and click Sensor or Start NPC when done. // :CODE: +// Rev 1.6 5-24-2014 + +// Rev 1.1 10-2-2014 @Sit did not work. Minor tweaks to casting for lslEditor +// Rev 1.2 10-14-2014 @ sit had wrong type. +// Rev 1.3 relative movement fixed for @fly +// Rev 1.4 4-3-2014 allow anyone to use this, non owners and non group members can only start and stop. +// Rev 1.5 5-17-2014 set sensor to auto start on reboot of sim +// Rev 1.6 5-24-2014 move menu so you can get it by touching, removed many of the KeyValues to RAM for efficiency + //*******************************************************************// // Instructions on how to use this is at http://www.free-lsl-scripts.com/opensim/posts/NPC/ // This is an OpenSim-only script. -// Author: Fred Beckhusen (Ferd Frederix) +// Author: Ferd Frederix //////////////////////////////////////////////////////////////////////////////////////////// @@ -28,7 +38,7 @@ // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////////////////////// // The original NPC controller was from http://was.fm/opensim:npc -// Extensive additions and bug fixes by Fred Beckhusem, aka Fred Beckhusen (Ferd Frederix), fred@mitsi.com +// Extensive additions and bug fixes by Fred Beckhusem, aka Ferd Frederix, fred@mitsi.com // llSensor had two params swapped // @Wander would wander where it had rezzed, not where it was. // There was no 'no_sensor' event in sit, so if a @sit failed, the NPC got stuck diff --git a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Path b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Path index ea4910bd..e59edbbe 100644 --- a/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Path +++ b/NPC Chatbot for Opensim/NPC Chatbot for Opensim/Object/Path @@ -1,17 +1,5 @@ -// :SHOW: -// :CATEGORY:ChatBot -// :NAME:NPC Chatbot for Opensim -// :AUTHOR:Ferd Frederix -// :KEYWORDS: -// :CREATED:2016-07-27 16:14:00 -// :EDITED:2016-07-27 15:14:00 -// :ID:1108 -// :NUM:1908 -// :REV:1.0 -// :WORLD:OpenSim -// :DESCRIPTION: // Sample Path notecard -// :CODE: + @spawn=Tinker@www.outworldz.com|<0.000000,0.000000,0.500000> @walk=<0,-8,0.5> @sit=LayDown diff --git a/Opensim Train/Train/Opensim Train/Object/NoteCard.txt b/Opensim Train/Train/Opensim Train/Object/NoteCard.txt new file mode 100644 index 00000000..2b991fc3 --- /dev/null +++ b/Opensim Train/Train/Opensim Train/Object/NoteCard.txt @@ -0,0 +1,9 @@ +SOME NOTES ABOUT THIS SCRIPT + +I'm more of a script hacker than a script editor, and I decided to hack this script. I got rid of the Guide prims and got it to run ON the MESH TRACK! I did this by editing the script and replacing the value of gsSensorTargetName from Guide to Freight. Then I renamed each mesh track to Freight. I found this mostly worked just as well. + +Of course when laying track there's issues for sharp curves and sim borders, no matter what the Guides are called. On the sharp curves I didn't rename the track but simply added the old guides and renamed them as Freight, adding enough for the engine to take the curve. On the sim boarder I put two guides - one on either side - so that their centres were close to the boarder. If I didn't have the curve guides close enough to each other and the straight track, or close enough to the boarder, the script assumes it's the end of the line and reverses! Also, the mesh track's 18.5m long. I extended the rail line by changing to LOCAL mode, duplicating a Freight by pressing SHIFT and moving it 1/2 its length along, then changing its horizontal rotation slightly, and moving it along by another 1/2 length. This works up to 18.5m long, but 20m breaks the line. + +Why use Freight instead of Guide? It occurred to me the key to this script is how the script finds the next guide in the rail line, and that's done by looking for the correct name. But why only have ONE name for guides. You could have several overlapping transparent sets of guides, each one with the name of the service that runs on it. e.g. "Freight" and "Passenger". Ideally it's be good if for example you could also have a "Shared" guide that different services all use, but I'm not sure how to make that happen yet. Also, perhaps when an engine gets to the end of the line and detects that, it not only reverses the engine, but changes gsSensorTargetName to a different value (e.g. "Up" is changed to "Down") and then it returns down a DIFFERENT track/guides! + +Just some ideas. I'm excited that the script works. I really need to learn LSL. \ No newline at end of file diff --git a/Opensim Train/Train/Opensim Train/Object/Opensim Train.lsl b/Opensim Train/Train/Opensim Train/Object/Opensim Train.lsl new file mode 100644 index 00000000..ce2961c8 --- /dev/null +++ b/Opensim Train/Train/Opensim Train/Object/Opensim Train.lsl @@ -0,0 +1,1518 @@ +// :SHOW: +// :CATEGORY:Train +// :NAME:Opensim Train +// :AUTHOR:Moundsa Mayo +// :KEYWORDS:Opensim Train +// :REV:2.6.0 +// :WORLD:Opensim +// :DESCRIPTION: +// Opensim Train +// :CODE: + + +// [VRCLocomotiveOpensourceScript] +string gsScriptVersion = "2.6.0"; + + +// Created for VRC distribution by Moundsa Mayo +// +// Based on the original Opensource Hobo Train Script +// written by Twisted Laws sometime in 2007 or 2008 +// +// Contact the VRC for the original script unaltered along with some +// revision history removed from this version. +// + + +// train driver script example for SLRR. +// +// this is setup for non-physical, phantom movement but can be +// modified for physical, etc. + + +//===================================================================// +//==== Global Constants ============================ ====// + +integer INDEX_INVALID = -1; +integer LINK_INVALID = -32768; +string NAME_WILDCARD = ""; + +vector COLOR_GREEN = <0.00, 1.00, 0.00>; +vector COLOR_BLACK = <0.00, 0.00, 0.00>; + +//==== Global Constants ============================== ====// +//===================================================================// + + +//===================================================================// +//==== Utility Routines ============================ ====// + +string gsHexPrefix = "0x"; +string gsHexChars = "0123456789ABCDEF"; + +string Int2Hex(integer iInt, integer iDigits) +{ + integer iWork = iInt & 0xF; + string sResult = llGetSubString(gsHexChars, iWork, iWork); + iInt = (iInt >> 4) & 0x0FFFFFFF; + + while (iInt != 0) + { + iWork = iInt & 0xF; + sResult = llGetSubString(gsHexChars, iWork, iWork) + sResult; + iInt = iInt >> 4; + } + + if (llStringLength(sResult) < iDigits) + { + sResult = "00000000" + sResult; + sResult = llGetSubString(sResult, -iDigits, - 1); + } + + return(gsHexPrefix + sResult); +} // string Int2Hex + + +// Force unsit all sitting agents +integer UnsitAllAgents() +{ + integer iIndex; + list lLinkDetails = []; + integer iAgentCount = 0; + integer iLinkCount = llGetNumberOfPrims(); + + for (iIndex = 1; iIndex <= iLinkCount; ++iIndex) + { + lLinkDetails = llGetObjectDetails(llGetLinkKey(iIndex), [OBJECT_CREATOR]); + + // Probably an Agent, so unsit and add to count + if (llList2Key(lLinkDetails, 0) == NULL_KEY) + { + ++iAgentCount; + llUnSit(llGetLinkKey(iIndex)); + } + + } // for all Links in Object + + return(iAgentCount); +} // UnsitAllAgents + + +// Sum up prims in linkset, omitting any sitting agents +// (llGetNumberOfPrims includes sitting Agents in the returned count) +integer PrimCount() +{ + integer iIndex; + list lLinkDetails = []; + integer iPrimCount = 0; + integer iLinkCount = llGetNumberOfPrims(); + + for (iIndex = 1; iIndex <= iLinkCount; ++iIndex) + { + lLinkDetails = llGetObjectDetails(llGetLinkKey(iIndex), + [OBJECT_CREATOR]); + + // Probably not an Agent, so add to count + if (llList2Key(lLinkDetails, 0) != NULL_KEY) + { + ++iPrimCount; + } + + } // for all Links in Object + + // llOwnerSay("Links=" + (string)iPrimCount); + return(iPrimCount); +} // PrimCount + + +string clip(float value) +{ + string str = (string)value; + integer where = llSubStringIndex(str, "."); + return(llGetSubString(str, 0, where + 2)); +} // clip + + +integer NamedLinkFind(string sName) +{ + // locate a linked prim + integer i; + integer m = llGetNumberOfPrims(); + integer ret= LINK_INVALID; + + for (i = 1; i <= m; i++) + { + + if (llGetLinkName(i) == sName) + { + ret = i; + } + + } + + return ret; +} // NamedLinkFind + + +key ScanForAgentGone() +{ + key kLinkID; + + integer iIndex; + integer iRiderRollCount = llGetListLength(glRidersAboard); + + // Scan Rider roll for ID missing from linked objects beyond + // original object linkset. Assume any missing are AGENT + list lRiderLinks = []; + + for (iIndex = giLinkCountRiderless + 1; iIndex <= + giLinkCountCurrent; ++iIndex) + { + kLinkID = llGetLinkKey(iIndex); + lRiderLinks = lRiderLinks + (list)kLinkID; + } + + for (iIndex = 0; iIndex < iRiderRollCount; ++iIndex) + { + kLinkID = llList2Key(glRidersAboard, iIndex); + + if (llListFindList(lRiderLinks, [kLinkID]) == INDEX_INVALID) + { + return(kLinkID); + } + + } // for all high links (sitting atatars) + + return(NULL_KEY); +} // ScanForAgentGone + +//==== Utility Routines ============================== ====// +//===================================================================// + + +//===================================================================// +//==== Locomotive-Specific Values & Code =========== ====// + +// This code subsection defines a list of locomotive features encoded +// as single-bit values into an integer. The feature set controls +// which buttons are included in the Engineer's Menu and some aspects +// of the locomotive's operation. + +integer FEATURE_SOUND_ENGINE = 0x00000001; +integer FEATURE_SOUND_WHEELS = 0x00000002; +integer FEATURE_SOUND_STEAM = 0x00000004; +integer FEATURE_SOUND_RESERVED = 0x00000008; + +integer FEATURE_ALERT_BELL = 0x00000010; +integer FEATURE_ALERT_WHISTLE = 0x00000020; +integer FEATURE_ALERT_HORN = 0x00000040; +integer FEATURE_ALERT_RESERVED = 0x00000080; + +integer FEATURE_LIGHTS_HEADLAMPS = 0x00000100; +integer FEATURE_LIGHTS_TAILLAMPS = 0x00000200; +integer FEATURE_LIGHTS_RUNNING = 0x00000400; +integer FEATURE_LIGHTS_RESERVED = 0x00000800; + +integer FEATURE_CONTROL_SMOKE = 0x00010000; + +integer FEATURE_MOTION_NONPHYSICAL = 0x00100000; +integer FEATURE_MOTION_PHYSICAL = 0x00200000; +integer FEATURE_MOTION_FLIP = 0x00400000; +integer FEATURE_MOTION_REVERSE = 0x00800000; + +integer FEATURE_DISPLACE_RERAIL = 0x01000000; +integer FEATURE_DISPLACE_LEFT = 0x02000000; +integer FEATURE_DISPLACE_RIGHT = 0x04000000; + + +// Compute later due to LSL limitation +rotation ROTATION_FLIP = ZERO_ROTATION; + + +// This value defines the 'standard' featureset implemented in the VRC +// Opensource Locomotive Script. TO remove a feature from a particular +// build, Boolean (bitwise) AND the NOT of the feature code with the +// FEATURESET_DEFAULT value. (See "VRC 2010 Train" entry in +// configuration table below) + +// Compute at initialization time due to LSL limitation. Cannot include +// in table due to LSL design. Instead, table can include a SINGLE +// feature to be REMOVED from FEATURESET_DEFAULT +integer FEATURESET_DEFAULT = 0; + + +// This code subsection defines a list-based table of locomotive +// builds identified by the first words in their ObjectName. Several +// configuration values are preset in the table for selection by build +// name +// +// Any build found in the table at script initialization time will be +// assigned the values following its BUILD_TAG. +// +// Any build not identified in the table will be assigned the default +// values. + +integer UNIT_BUILD_TAG_IDX = 0; +integer UNIT_BUILD_GUIDE_OFFSET_IDX = 1; +integer UNIT_BUILD_SIT_OFFSET_IDX = 2; +integer UNIT_BUILD_SIT_ROTATION_IDX = 3; +integer UNIT_BUILD_SPEED_MINIMUM_IDX = 4; +integer UNIT_BUILD_SPEED_MAXIMUM_IDX = 5; +integer UNIT_BUILD_SPEED_INCREMENT_IDX = 6; +integer UNIT_BUILD_FEATURE_ADJUST_IDX = 7; + +integer UNIT_BUILD_TABLE_STRIDE = 8; + +// string sBuildTag, vector vGuideOffset, vector vSitOffset, +// rotation rSitRotation +list glUnitConfiguration = [ + + // DEFAULT BUILD - probably won't work well for ANY build + "DEFAULT BUILD", <0.00, 0.00, 0.500>, <-4.000, -0.500, 1.000>, + ZERO_ROTATION, 0.05, 8.00, 0.05, 0, + + // Desmond Shang + "VRC Hobo Train", <0.00, 0.00, 0.585>, <-3.50816, -0.500, 1.000>, + <-0.00876, -0.00003, 0.00000, 0.99996>, 0.0050, 0.1000, 0.0050, + 0, + + // Lotek Ixtar + // "NS2200", <0.00, 0.00, 0.720>, <-3.88150, -0.73620, 1.51784>, + "[ix] NS 2201", <0.00, 0.00, 0.625>, + <-3.594925, -1.217773, 1.880998>, + <0.00000, 0.00000, 0.00000, -1.00000>, + 0.005, 0.05, 0.005, + 0x10034, + + // Veryfluffy Wingtips + "Fluffies Diesel", <0.00, 0.00, 0.910>, <-1.95, 0.850, 0.950>, + ZERO_ROTATION, 0.25, 10.00, 0.25, 0, + + // Garden Mole + "Great Western 4500", <0.00, 0.00, 0.917>, + <-3.50816, -0.500, 1.475>, ZERO_ROTATION, 0.10, 8.00, 0.10, + 0 + + ]; + + +//==== Operator Preference Subsection ============== ====// + +// This feature may be unacceptable at higher speeds or if subject to +// motion sickness, so it is disable by default + +//integer CAMERA_DYNAMICS_ENABLED = TRUE; +integer CAMERA_DYNAMICS_ENABLED = FALSE; + +//==== Operator Preference Subsection ================ ====// + + +// CAUTION: Be VERY careful when adjusting these values - they are +// set to meet SLRR published track layout standards and +// have been thoroughly tested on the SLRR + +// Scan radius in meters +float SENSOR_SEEK_RANGE_DEFAULT = 20.00; + +// Scan half-angle in degrees. Arc swept is this amount on either +// side of X-axis of sensor prim +float SENSOR_SEEK_ARC_DEFAULT = 25.00; + + +string OBJECT_DESCRIPTION_TAG = "Script Version "; +string VERSION_PREFIX_TAG = " Opensource V"; +string ENGINEER_MENU_INSTRUCTION = "Touch train for Engineer's Menu"; + +string SIT_TEXT_OPERATOR = "Engineer"; +string SIT_TEXT_PASSENGER = "Passenger"; + +// Buttons for Engineer's menu for all builds and script revisions +string BTN_START = "Start"; +string BTN_IDLE = "Idle"; +string BTN_STOP = "Stop"; +string BTN_FASTER = "Faster"; +string BTN_SLOWER = "Slower"; +string BTN_FLIP = "Flip"; +string BTN_HELP = "Help"; + +// Buttons for Engineer's menu constructed according to featureset +// Not all buttons or related functionality are implemented in a given +// revision of this script +string BTN_BLANK = " "; +string BTN_BELL = "Bell"; +string BTN_WHISTLE = "Whistle"; +string BTN_HORN = "Horn"; +string BTN_HEADLAMP = "Headlamp"; +string BTN_RERAIL = "Rerail"; +string BTN_NONPHYSICAL = "NONPhysical"; +string BTN_PHYSICAL = "Physical"; + +string BTN_SMOKE = "Smoke"; + + +// Name the smoke-emitting prim SMOKE_PRIM_NAME +string SMOKE_PRIM_NAME = "Smoke"; +integer SMOKE_OFF = 0; +integer SMOKE_IDLE = 1; +integer SMOKE_RUN = 2; + + +integer SIT_ALLOW = TRUE; +integer SIT_DENY = FALSE; + +integer ENGINE_STATUS_OFF = 0; +integer ENGINE_STATUS_IDLING = 1; +integer ENGINE_STATUS_ROLLING = 2; + +// Name the Engineer's display prim ENGINEERDISPLAY_PRIM_NAME +string ENGINEERDISPLAY_PRIM_NAME = "EngineerDisplay"; + +string gsLocomotiveNameTag = ""; +integer giLocomotiveFeatureSet = 0; +integer giLocomotiveFeatureAdjust = 0; +vector gvUnitToGuideOffset = ZERO_VECTOR; +vector gvSitOffset = ZERO_VECTOR; +rotation grSitOrientation = ZERO_ROTATION; +integer giEngineStatus = 0; +integer giNoHitsCount = 0; + +// Default for SLRR ROW +string gsSensorTargetName = "Guide"; + +float gfSensorSeekRange = 20.00; +float gfSensorScanArc = 0; +integer giTravelDirection = 0; +integer giFlipRequest = 0; +vector gvWaypointPrior = ZERO_VECTOR; + +float gfSpeedPrior = 0.00; +float gfSpeedCurrent = 0.00; +float gfSpeedMinimum = 0.00; +float gfSpeedMaximum = 0.00; +float gfSpeedIncrement = 0.00; + +integer giStatusPhysical = TRUE; +integer giFlagPhantom = TRUE; +string gsStatusDisplayPrior = ""; + +integer giRerailRequest = TRUE; + +string gsDisplayPrimName = "EngineerDisplay"; +integer giDisplayPrimLink = -32768; + +integer giLinkCountRiderless = 0; +integer giLinkCountPrior = 0; +integer giLinkCountCurrent = 0; +integer giLinkGained = FALSE; +string gsRegionCurrentName; +vector gvRegionCurrentCorner; +integer giSmokeEnabled = TRUE; +integer giSmokeOn = TRUE; +integer giDialogChannel = PUBLIC_CHANNEL; +key gkOwnerID = NULL_KEY; +key gkOperatorID = NULL_KEY; +key gkPassengerID = NULL_KEY; +integer giOperatorSeated = FALSE; +integer giCameraDynamics = FALSE; + +// Table of all agents seated +list glRidersAboard =[]; + +// Default Buttonset in all menu versions. At initialization time +// build-specific feature buttons may be added as indiczted by +// giFeatureSet +list glMenuOperatorDefault = [ + "HELP", + "FASTER", "SLOWER", "FLIP", + "START", "IDLE", "STOP" + ]; + +list glMenuOperator = []; + + +// These are activated if set in Operator Preference section +list glCameraParameters = [ + CAMERA_ACTIVE, TRUE, + + CAMERA_BEHINDNESS_ANGLE, 0.00, + CAMERA_BEHINDNESS_LAG, 0.50, + CAMERA_DISTANCE, 7.00, + CAMERA_PITCH, 15.00, + + // CAMERA_FOCUS, + CAMERA_FOCUS_LAG, 0.50, + CAMERA_FOCUS_LOCKED, FALSE, + CAMERA_FOCUS_THRESHOLD, 0.00, + + // CAMERA_POSITION, + CAMERA_POSITION_LAG, 0.50, + CAMERA_POSITION_LOCKED, FALSE, + CAMERA_POSITION_THRESHOLD, 0.05, + + CAMERA_FOCUS_OFFSET, <0.00, 0.00, 1.00> + ]; + + +//===================================================================// +//==== Locomotive-Specific Routines == ==================// + +list LocomotiveMenuCreateFromFeatureset(integer iFeatureSet) +{ + list lMenu = []; + + // llOwnerSay("$00F0: FeatureSet=" + Int2Hex(iFeatureSet, 8)); + + if (iFeatureSet & FEATURE_CONTROL_SMOKE) + { + lMenu = BTN_SMOKE + lMenu; + } + + if (iFeatureSet & FEATURE_ALERT_BELL) + { + lMenu = BTN_BELL + lMenu; + } + + if (iFeatureSet & FEATURE_ALERT_HORN) + { + lMenu = BTN_HORN + lMenu; + } + + // Temporary fix to accomodate an exception. Later + // version will provide more flexible menu construction + else + { + lMenu = BTN_BLANK + lMenu; + } + + if (iFeatureSet & FEATURE_ALERT_WHISTLE) + { + lMenu = BTN_WHISTLE + lMenu; + } + + if (iFeatureSet & FEATURE_LIGHTS_HEADLAMPS) + { + lMenu = BTN_HEADLAMP + lMenu; + } + + if (~llGetListLength(glMenuOperator)) lMenu = lMenu + glMenuOperator; + // llOwnerSay("Menu=" + llList2CSV(lMenu)); + + return(lMenu); +} // LocomotiveMenuCreateFromFeatureset + + +LocomotiveBuildConfigure() +{ + integer iBuild; + integer iBuildItems = llGetListLength(glUnitConfiguration); + integer iBuildIndex = INDEX_INVALID; + string sBuildTag; + string sBuildName = llGetObjectName(); + + // Set to a default feature set, from which a single feature may later be removed + integer giLocomotiveFeatureSet = FEATURE_SOUND_ENGINE | + FEATURE_SOUND_WHEELS | FEATURE_SOUND_STEAM | FEATURE_ALERT_BELL | + FEATURE_ALERT_WHISTLE | FEATURE_ALERT_HORN | + FEATURE_LIGHTS_HEADLAMPS | FEATURE_LIGHTS_TAILLAMPS | + FEATURE_CONTROL_SMOKE | FEATURE_MOTION_PHYSICAL; + // FEATURE_CONTROL_SMOKE | FEATURE_MOTION_NONPHYSICAL; + + for (iBuild = 0; iBuild < iBuildItems; iBuild = iBuild + + UNIT_BUILD_TABLE_STRIDE) + { + sBuildTag = llList2String(glUnitConfiguration, iBuild + + UNIT_BUILD_TAG_IDX); + + if (llSubStringIndex(sBuildName, sBuildTag) == 0) + { + iBuildIndex = iBuild; + gsLocomotiveNameTag = sBuildTag; + } + + } // for all builds in table + + + if (iBuildIndex == INDEX_INVALID) + { + iBuildIndex = 0; + } + + gvUnitToGuideOffset = llList2Vector(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_GUIDE_OFFSET_IDX); + gvSitOffset = llList2Vector(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SIT_OFFSET_IDX); + grSitOrientation = llList2Rot(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SIT_ROTATION_IDX); + gfSpeedMinimum = llList2Float(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SPEED_MINIMUM_IDX); + gfSpeedIncrement = llList2Float(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SPEED_INCREMENT_IDX); + gfSpeedMaximum = llList2Float(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_SPEED_MAXIMUM_IDX); + giLocomotiveFeatureAdjust = llList2Integer(glUnitConfiguration, + iBuildIndex + UNIT_BUILD_FEATURE_ADJUST_IDX); + + llOwnerSay("$0100: Buildname=" + sBuildName + + " Build=" + (string)iBuildIndex + + ":" + gsLocomotiveNameTag + + " FeatureSet=" + Int2Hex(giLocomotiveFeatureSet, 8) + + " FeatureAdjust=" + Int2Hex(giLocomotiveFeatureAdjust, 8) + + " AdjustedFeatureSet=" + Int2Hex(giLocomotiveFeatureSet & + (~giLocomotiveFeatureAdjust), 8)); + + // Adjust the default featureset by removing any single + // unwanted feature. Need greater flexibilty here. + giLocomotiveFeatureSet = giLocomotiveFeatureSet & + (~giLocomotiveFeatureAdjust); + glMenuOperator = LocomotiveMenuCreateFromFeatureset( + giLocomotiveFeatureSet); + + // llOwnerSay("$0110: CONFIG=" + (string)gvUnitToGuideOffset + ", " + + // (string)gvSitOffset + ", " + (string)grSitOrientation + + // ", " + (string)gfSpeedMinimum + ", " + + // (string)gfSpeedMaximum + ", " + (string)gfSpeedIncrement); + +} // LocomotiveBuildConfigure + + +string LocomotiveHelpBuild() +{ + string sHelpString = ""; + + sHelpString += "\n Page Up = Stopped: Idle->Running->Faster"; + sHelpString += "\n Page Down = Running: Slower->Idle->Stop"; + sHelpString += "\n Shift + Right Arrow = Instant Idle/Resume"; + // sHelpString += "\nDown Arrow = Reverse"; + sHelpString += "\n Shift + Left Arrow = Flip train around"; + return(sHelpString); +} // LocomotiveHelpBuild + + +LocomotiveDisplaySet(string str, vector color) +{ + + if (gsStatusDisplayPrior != str) + { + gsStatusDisplayPrior = str; + llSetLinkPrimitiveParamsFast(giDisplayPrimLink, [ + PRIM_TEXT, gsStatusDisplayPrior, color, 1.0]); + } + +} // LocomotiveDisplaySet + + +LocomotiveDisplayUpdate() +{ + vector color = <0.5, 1.0, 0.5>; + string str = "Speed: " + clip(gfSpeedCurrent * 1000) + "\n"; + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + str += "\nRunning"; + } + else if (giEngineStatus == ENGINE_STATUS_IDLING) + { + str += "\nIdling"; + } + else + { + str = llGetObjectName() + "\n\n"; + str += "\nStopped"; + color = <0.5, 0.5, 1.0>; + } + + if (llGetStatus(STATUS_PHANTOM)) + { + str += "\nPhantom"; + } + else + { + str += "\nNOT Phantom!!"; + } + + str += "\nFollowing '" + gsSensorTargetName + "'"; + + if (giLinkCountCurrent != giLinkCountRiderless) + { + str += "\n \n \n" + ENGINEER_MENU_INSTRUCTION; + } + + LocomotiveDisplaySet(str, color); +} // LocomotiveDisplayUpdate + + +integer LocomotiveRiderAboard(key gkRiderID) +{ + return(NamedLinkFind(llKey2Name(gkRiderID)) != LINK_INVALID); +} // LocomotiveRiderAboard + + +LocomotivePassengerRejection(key kAgentID, integer iSitAllow) +{ + + if (iSitAllow) + { + llInstantMessage(kAgentID, "\nSorry, " + llKey2Name(kAgentID) + + ", only the Owner may operate this locomotive."); + } + else + { + llInstantMessage(kAgentID, "\nSorry, " + llKey2Name(kAgentID) + + ", only the Owner may operate this locomotive." + + "\nAnd the engineer needs to climb aboard first"); + llUnSit(kAgentID); + } + +} // LocomotivePassengerRejection + + +LocomotiveMoveTo(vector vWaypointNext, rotation rAttitudeNext) +{ + + if (llGetStatus(STATUS_PHYSICS)) + { + llRotLookAt(rAttitudeNext, 1 / llVecMag(llGetVel()), 1); + llMoveToTarget( vWaypointNext, 1.0); + } + else + { + llSetLinkPrimitiveParamsFast(LINK_ROOT, + [PRIM_POSITION, vWaypointNext, + PRIM_ROTATION, rAttitudeNext]); + } + +} // LocomotiveMoveTo + +// Opensim sim-crossing compatibility +LocomotiveMoveToSlow(vector vWaypointNext, rotation rAttitudeNext) +{ + if (llGetStatus(STATUS_PHYSICS)) { + llRotLookAt(rAttitudeNext, 1 / llVecMag(llGetVel()), 1); + llMoveToTarget( vWaypointNext, 1.0); + } else { + llSetPos(vWaypointNext); + llSetRot(rAttitudeNext); + } +} + +LocomotiveFlip() +{ + giStatusPhysical = llGetStatus(STATUS_PHYSICS); + + if (giStatusPhysical) + { + llSetStatus(STATUS_PHYSICS,FALSE); + } + + LocomotiveDisplaySet("reverse", <1.0, 0.5, 0.5>); + giFlipRequest = FALSE; + // llSetRot(ROTATION_FLIP llGetRot() ); + llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_ROTATION, (ROTATION_FLIP*llGetRot()) ]); + giTravelDirection = (giTravelDirection + 1) % 2; + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + llSleep(0.5); + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + } + + if (giStatusPhysical) + { + llSetStatus(STATUS_PHYSICS,TRUE); + } + +} // LocomotiveFlip + + +float LocomotiveSpeedUpdate(float fSpeedDelta) +{ + // llOwnerSay("SD=" + (string)fSpeedDelta); + + if (fSpeedDelta > 0.00) + { + + if ( (gfSpeedCurrent + fSpeedDelta) <= gfSpeedMaximum) + { + gfSpeedCurrent += fSpeedDelta; + } + + } + + else + { + + if ( (gfSpeedCurrent + fSpeedDelta) > 0) + { + gfSpeedCurrent += fSpeedDelta; + } + + } + + return(gfSpeedCurrent); +} // LocomotiveSpeedUpdate + + + +LocomotiveSpeedUp() +{ + + if (giEngineStatus == ENGINE_STATUS_OFF) + { + LocomotiveIdle(); + } + else if (giEngineStatus == ENGINE_STATUS_IDLING) + { + LocomotiveRun(gfSpeedMinimum); + } + else if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + LocomotiveSpeedUpdate(gfSpeedIncrement); + } + +} // LocomotiveSpeedUp + + +LocomotiveSlowDown() +{ + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + + if (gfSpeedCurrent > gfSpeedMinimum) + { + LocomotiveSpeedUpdate(-gfSpeedIncrement); + } + else + { + LocomotiveIdle(); + } + + } + else if (giEngineStatus == ENGINE_STATUS_IDLING) + { + LocomotiveStop(); + } + +} // LocomotiveSlowDown + + +LocomotiveIdle() +{ + llMessageLinked(LINK_SET, 99, "idle", (key)NAME_WILDCARD); + giSmokeOn = LocomotiveSmoke(SMOKE_IDLE); + llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_POSITION,llGetPos()+<-0.01,0.0,0.0>]); + giEngineStatus = ENGINE_STATUS_IDLING; + gfSpeedPrior = gfSpeedCurrent; + gfSpeedCurrent = 0.00; + // llVolumeDetect(TRUE); + giFlagPhantom = TRUE; +} // LocomotiveIdle + + +LocomotiveRun(float fSpeedStartup) +{ + llMessageLinked(LINK_SET, 99, "run", (key)NAME_WILDCARD); + giSmokeOn = LocomotiveSmoke(SMOKE_RUN); + llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_POSITION,llGetPos()+<-0.01,0.0,0.0>]); + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + giEngineStatus = ENGINE_STATUS_ROLLING; + gfSpeedCurrent = fSpeedStartup; + // llVolumeDetect(TRUE); + giFlagPhantom = TRUE; +} // LocomotiveRun + + +LocomotiveStop() +{ + gfSpeedCurrent = 0.00; + llMessageLinked(LINK_SET, -1, "reset", (key)NAME_WILDCARD); + giSmokeOn = LocomotiveSmoke(SMOKE_OFF); + giStatusPhysical = llGetStatus(STATUS_PHYSICS); + llSetStatus(STATUS_PHYSICS, FALSE); + llSensorRemove(); + giEngineStatus = ENGINE_STATUS_OFF; + giFlagPhantom = FALSE; + // llVolumeDetect(FALSE); + llSetStatus(STATUS_PHANTOM, TRUE); +} // LocomotiveStop + + +integer LocomotiveSmoke(integer iSmokeLevel) +{ + integer iSmokeNew = TRUE; + + if (giSmokeEnabled) + { + llMessageLinked(LINK_ALL_CHILDREN, iSmokeLevel, + SMOKE_PRIM_NAME, NULL_KEY); + } + else + { + llMessageLinked(LINK_ALL_CHILDREN, SMOKE_OFF, + SMOKE_PRIM_NAME, NULL_KEY); + } + + if (iSmokeLevel == SMOKE_OFF) + { + iSmokeNew = FALSE; + } + + return(iSmokeNew); +} // LocomotiveSmoke + +//==== Locomotive-Specific Routines == ====================// + + +ScriptInitialize() +{ + ROTATION_FLIP = llEuler2Rot(<0.00, 0.00, 180.00> * DEG_TO_RAD); + + integer iLinkCount = llGetNumberOfPrims(); + + // TO clear any existing sit targets, uncomment the code below: + // + // integer iIndex; + // for (iIndex = 0; iIndex > iLinkCount; iIndex++) + // { + // llSetText(".", COLOR_BLACK, 0.00); + // llSitTarget(ZERO_VECTOR, ZERO_ROTATION); + // } + + UnsitAllAgents(); + llSetClickAction(CLICK_ACTION_SIT); + giLinkCountRiderless = PrimCount(); + giLinkCountCurrent = llGetNumberOfPrims(); + giLinkCountPrior = giLinkCountCurrent; + gsRegionCurrentName = llGetRegionName(); + gvRegionCurrentCorner = llGetRegionCorner(); + gfSensorScanArc = SENSOR_SEEK_ARC_DEFAULT * DEG_TO_RAD; + gkOwnerID = llGetOwner(); + llMessageLinked(LINK_SET, -1, "reset", (key)NAME_WILDCARD); + llSetBuoyancy(1.0); + llStopMoveToTarget(); + llStopLookAt(); + llSetText(" ",<1.00, 1.00, 1.00>, 1.00); + + llSetStatus(STATUS_PHANTOM,TRUE); + giDisplayPrimLink = NamedLinkFind(gsDisplayPrimName); + // llOwnerSay("Display=" + (string)giDisplayPrimLink + " " + + // llGetLinkName(giDisplayPrimLink)); + LocomotiveDisplaySet("-----", <0.50, 0.50, 1.00>); + + llSetObjectDesc(""); + + // For development and testing + // llSetObjectName(SET TO BUILD NAME TO TEST AGAINST); + + glMenuOperator = glMenuOperatorDefault; + LocomotiveBuildConfigure(); + llSetObjectName(gsLocomotiveNameTag + VERSION_PREFIX_TAG + + gsScriptVersion); + llSetSitText(SIT_TEXT_OPERATOR); + llSitTarget(gvSitOffset, grSitOrientation); + llSetCameraAtOffset(<3.00, 0.00, 1.50>); + llSetCameraEyeOffset(<-8.00, 0.00, 5.00>); + LocomotiveDisplayUpdate(); + + key agent = llAvatarOnSitTarget(); + + if (agent != NULL_KEY) + { + llRequestPermissions(agent, PERMISSION_TAKE_CONTROLS); + // llVolumeDetect(TRUE); + } + +} // ScriptInitialize + + + +default +{ + + state_entry() + { + + if (gkOperatorID == NULL_KEY) + { + ScriptInitialize(); + } + + if (giRerailRequest) + { + state Rerail; + } + + giDialogChannel = -llGetUnixTime(); + llListen(giDialogChannel, "", "", ""); + giSmokeOn = LocomotiveSmoke(SMOKE_OFF); + LocomotiveDisplayUpdate(); + } // state_entry + + + // Only recognizes first Toucher + touch_start(integer iTouching) + { + + if (giLinkCountCurrent > giLinkCountRiderless) + { + + if (llDetectedKey(0) == gkOperatorID) + { + string sPrompt = "\nSmoke "; + + if (giSmokeEnabled) + { + sPrompt += "ENABLED"; + } + else + { + sPrompt += "DISABLED"; + } + + sPrompt += "\n\nOperate locomotive"; + llDialog(gkOperatorID, sPrompt, + glMenuOperator, giDialogChannel); + } + // else + // { + // LocomotivePassengerRejection(llDetectedKey(0), + // SIT_ALLOW); + // } + + } + + } // touch_start + + + changed(integer iChanged) + { + + if (iChanged & CHANGED_REGION) + { + gsRegionCurrentName = llGetRegionName(); + gvRegionCurrentCorner = llGetRegionCorner(); + LocomotiveRun(gfSpeedCurrent); + LocomotiveDisplayUpdate(); + } + + if (iChanged & CHANGED_LINK) + { + key kRiderID = NULL_KEY; + + giLinkCountCurrent = llGetNumberOfPrims(); + + // Gained a link + if (giLinkCountCurrent > giLinkCountPrior) + { + giLinkGained = TRUE; + giLinkCountPrior = giLinkCountCurrent; + kRiderID = llGetLinkKey(giLinkCountCurrent); + glRidersAboard = glRidersAboard + (list)kRiderID; + + // If driver needed. + if (gkOperatorID == NULL_KEY) + { + + // owner only can ride + if (kRiderID == gkOwnerID) + { + // seat as driver + gkOperatorID = kRiderID; + llSetSitText(SIT_TEXT_PASSENGER); + llSetClickAction(CLICK_ACTION_TOUCH); + llVolumeDetect(TRUE); + llRequestPermissions(gkOperatorID, + PERMISSION_TRIGGER_ANIMATION | + PERMISSION_TAKE_CONTROLS | + PERMISSION_CONTROL_CAMERA); + } + else + { + LocomotivePassengerRejection(kRiderID, + SIT_DENY); + } + } + + // else seat as passenger + else + { + gkPassengerID = llGetLinkKey(giLinkCountCurrent); + // seat passenger at the right spot + llSetLinkPrimitiveParamsFast(giLinkCountCurrent, [ PRIM_POS_LOCAL, gvSitOffset+<.5,1,0.4>, PRIM_ROT_LOCAL, grSitOrientation ]); + // llSetCameraAtOffset(<3.00, 0.00, 1.50>); + // llSetCameraEyeOffset(<-8.00, 0.00, 5.00>); + llSetSitText(SIT_TEXT_OPERATOR); + llInstantMessage(gkPassengerID, "\nWelcome aboard, " + + llKey2Name(gkPassengerID) + "!" + + "\nYou can get your own train at:" + + "\n (todo: put hg address here)"); + } + + } // gained a link + + // Lost a link + else + { + giLinkGained = FALSE; + giLinkCountPrior = giLinkCountCurrent; + + // Scan the rider roll to see who is no longer linked + kRiderID = ScanForAgentGone(); + integer iRider = llListFindList(glRidersAboard, + (list)kRiderID); + + if (iRider != INDEX_INVALID) + { + glRidersAboard = llDeleteSubList(glRidersAboard, + iRider, iRider); + } + + // Engineer unsat + if (kRiderID == gkOperatorID) + { + giOperatorSeated = FALSE; + gkOperatorID = NULL_KEY; + llStopAnimation("sit"); + + if (llGetPermissions() & PERMISSION_TAKE_CONTROLS) + { + llReleaseControls(); + } + + if ( (giCameraDynamics) && + (llGetPermissions() & PERMISSION_CONTROL_CAMERA) ) + { + llClearCameraParams(); + } + + // Just leaving this commented here, as a warning. + // The following triggers a crash of the avatar after unseating: + llStopAnimation(llGetInventoryName(INVENTORY_ANIMATION, 0)); + + llSetClickAction(CLICK_ACTION_SIT); + llSetSitText(SIT_TEXT_OPERATOR); + llSitTarget(gvSitOffset, grSitOrientation); + // set camera for when engineer reseats + llSetCameraAtOffset(<3.00, 0.00, 1.50>); + llSetCameraEyeOffset(<-8.00, 0.00, 5.00>); + + } // Driver dismounted + + // Passenger dismounted + else + { + + if (gkOperatorID == NULL_KEY) + { + llSetSitText(SIT_TEXT_OPERATOR); + } + else + { + llSetSitText(SIT_TEXT_PASSENGER); + } + + gkPassengerID = NULL_KEY; + } // Passenger dismounted + + + } // lost a link + + // llOwnerSay("$1890: Riderless=" + + // (string)giLinkCountRiderless + + // " Prior=" + (string)giLinkCountPrior + + // " Current=" + (string)giLinkCountCurrent); + + // Force any count errors our way + if (giLinkCountCurrent < giLinkCountRiderless) + { + giLinkCountCurrent = giLinkCountRiderless; + } + + // If no-one aboard, stop locomotive + if ( ((giEngineStatus == ENGINE_STATUS_ROLLING) || + (giEngineStatus == ENGINE_STATUS_IDLING)) && + (giLinkCountCurrent == giLinkCountRiderless)) + { + LocomotiveStop(); + llSetSitText(SIT_TEXT_OPERATOR); + llSetClickAction(CLICK_ACTION_SIT); + } + + LocomotiveDisplayUpdate(); + } // CHANGED_LINK + + } // changed + + + run_time_permissions(integer iPermissions) + { + + if (iPermissions & PERMISSION_TAKE_CONTROLS) + { + // llTakeControls(CONTROL_UP | CONTROL_DOWN | CONTROL_FWD | + // CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT, + // TRUE, FALSE); + llTakeControls(CONTROL_UP | CONTROL_DOWN | CONTROL_LEFT | + CONTROL_RIGHT, TRUE, FALSE); + llInstantMessage(llAvatarOnSitTarget(), + "\n" + llKey2Name(llAvatarOnSitTarget()) + + ", " + ENGINEER_MENU_INSTRUCTION + + LocomotiveHelpBuild()); + } + + if ( (iPermissions & PERMISSION_TRIGGER_ANIMATION) && + (gkOperatorID != NULL_KEY) ) + { + llStopAnimation("sit"); + llStartAnimation(llGetInventoryName( + INVENTORY_ANIMATION, 0)); + llSetClickAction(CLICK_ACTION_TOUCH); + } + + if ( (giCameraDynamics) && + (iPermissions & PERMISSION_CONTROL_CAMERA) ) + { + llSetCameraParams(glCameraParameters); + } + + } // run_time_permissions + + + control(key kID, integer level, integer edge) + { + + if (edge & level & CONTROL_UP) + { + LocomotiveSpeedUp(); + } // CONTROL_UP + + + // if (edge & level & CONTROL_BACK) + // { + // Reserved for reverse/forward (as opposed to Flip + // } + + + if (edge & level & CONTROL_DOWN) + { + LocomotiveSlowDown(); + } // CONTROL_DOWN + + + if (edge & level & CONTROL_LEFT) + { + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + giFlipRequest=TRUE; + } + else + { + LocomotiveFlip(); + } + + } // CONTROL_LEFT + + + if (edge & level & CONTROL_RIGHT) + { + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + LocomotiveIdle(); + } + + else if (giEngineStatus == ENGINE_STATUS_IDLING) + { + LocomotiveRun(gfSpeedPrior); + } + + } // CONTROL_ROT_RIGHT + + LocomotiveDisplayUpdate(); + } // control + + + listen(integer iChannel, string sName, key kID, string sMessage) + { + + if ((sMessage == "Stop") && + ((giEngineStatus == ENGINE_STATUS_IDLING) || + giEngineStatus == ENGINE_STATUS_ROLLING) ) + { + LocomotiveStop(); + } // Stop + + + else if ( (sMessage == "Idle") && + ((giEngineStatus == ENGINE_STATUS_OFF) || + (giEngineStatus == ENGINE_STATUS_ROLLING)) ) + { + LocomotiveIdle(); + } // Idle + + + else if ((sMessage == "Start") && (giEngineStatus != + ENGINE_STATUS_ROLLING)) + { + LocomotiveRun(gfSpeedMinimum); + } // Start + + + else if (sMessage == "Flip") + { + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + giFlipRequest = TRUE; + } + else + { + LocomotiveFlip(); + } + + } // Flip + + + else if (sMessage == "Rerail") + { + state Rerail; + } // Rerail + + + else if (sMessage == "Faster") + { + LocomotiveSpeedUp(); + } // Faster + + + else if (sMessage == "Slower") + { + LocomotiveSlowDown(); + } // Slower + + else if (sMessage == "Smoke") + { + giSmokeEnabled = !giSmokeEnabled; + giSmokeOn = LocomotiveSmoke(giEngineStatus); + } // Smoke + + else if (sMessage == "Notecard") + { + llWhisper(PUBLIC_CHANNEL, "No Notecard available yet"); + } // Notecard + + else if (sMessage == "Help") + { + llOwnerSay(LocomotiveHelpBuild()); + } // Help + + else if ( (sMessage == "Bell") || (sMessage == "Horn") || + (sMessage == "Whistle") || (sMessage == "Headlamp") ) + { + llMessageLinked(LINK_SET, 0, sMessage, sMessage); + } // Bell | Horn | Whistle + + giStatusPhysical = llGetStatus(STATUS_PHYSICS); + LocomotiveDisplayUpdate(); + } // listen + + + sensor(integer iSensed) + { + + if (giEngineStatus == ENGINE_STATUS_ROLLING) + { + integer index = 0; + giNoHitsCount = 0; + + if (giFlipRequest) + { + LocomotiveFlip(); + return; + } + + if (llGetPos() == llDetectedPos(0) + gvUnitToGuideOffset) + { + + if (iSensed > 1) + { + index++; + } + else + { + vector next = llGetPos() + <15.00, 0.00, 0.00> *llGetRot(); + + if (next.x < 1.00 || next.x > 255.00 + || next.y < 1.00 || next.y > 255.00) + { + LocomotiveDisplaySet("sim crossing 1", + <0.50, 0.50, 1.00>); + LocomotiveMoveTo(llGetPos() + + <2.00, 0.00, 0.00> * llGetRot(), + llGetRot()); + } + else + { + LocomotiveFlip(); + LocomotiveDisplayUpdate(); + return; + } + + LocomotiveDisplayUpdate(); + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + return; + } + } + + rotation rHeadingNext = llDetectedRot(0); + + // Reverse Target Heading if travelling opposite to Guide + // X-axis polarity + if (giTravelDirection) + { + rHeadingNext = ROTATION_FLIP * rHeadingNext; + } + + // If angle between unit and next Guide greater than acceptable + // flip Target Heading 180 degrees + if (llFabs(llAngleBetween(rHeadingNext, llGetRot())) > PI_BY_TWO) + { + rHeadingNext = ROTATION_FLIP * rHeadingNext; + } + + + vector vPositionCurrent = llGetPos(); + vector vWaypointNext = llDetectedPos(index) + gvUnitToGuideOffset; + + if (llVecDist(vPositionCurrent, gvWaypointPrior) < + llVecDist(vPositionCurrent, vWaypointNext)) + { + rHeadingNext = llGetRot(); + } + else + { + gvWaypointPrior = vWaypointNext; + } + + vector vPositionNext = vPositionCurrent + (llVecNorm(vWaypointNext - + vPositionCurrent) * gfSpeedCurrent); + LocomotiveMoveTo(vPositionNext, rHeadingNext); + LocomotiveDisplayUpdate(); + + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + } // if running + + } // sensor + + + no_sensor() + { + + if (++giNoHitsCount > 4) + { + llResetScript(); + } + else + { + gsRegionCurrentName = llGetRegionName(); + LocomotiveDisplayUpdate(); + vector vPositionNext = llGetPos() + <15.00, 0.00, 0.00> * + llGetRot(); + + if (vPositionNext.x < 1.00 || vPositionNext.x > 255.00 + || vPositionNext.y < 1.00 || vPositionNext.y > 255.00) + { + LocomotiveDisplaySet("sim crossing 2", + <0.50, 0.50, 1.00>); + LocomotiveMoveToSlow(llGetPos() + <5.00, 0.00, 0.00>* + llGetRot(), + llGetRot()); + } + else + { + LocomotiveFlip(); + LocomotiveDisplayUpdate(); + return; + } + + LocomotiveDisplayUpdate(); + + // Second Life: + + // Give llSensor a rest + //llSleep(1.00); + + //llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + // gfSensorSeekRange, gfSensorScanArc); + + // XEngine in OpenSim doesn't like llSleep so we use + // a timer and do the llSensor there instead: + + llSetTimerEvent(1.0); + + } // if still seeking Guide + + } // no_sensor + + timer() + { + llSetTimerEvent(0); + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, gfSensorScanArc); + } + + on_rez(integer iRezParameter) + { + llResetScript(); + } // on_rez + + +} // state default + + + +state Rerail +{ + + state_entry() + { + LocomotiveDisplayUpdate(); + giRerailRequest = FALSE; + + // Note full-sphere sensor scan angle when seeking ROW + llSensor(gsSensorTargetName, "", ACTIVE | PASSIVE, + gfSensorSeekRange, PI); + gfSensorSeekRange = SENSOR_SEEK_RANGE_DEFAULT; + } // state_entry + + + sensor(integer iSensed) + { + // In theory, the first hit reported will always be the + // closest scan target detected + LocomotiveMoveTo(llDetectedPos(0) + gvUnitToGuideOffset, + llDetectedRot(0)); + state default; + } // sensor + + + no_sensor() + { + llOwnerSay("Scan target '" + gsSensorTargetName + + "' not within " + + (string)llRound(gfSensorSeekRange) + "m"); + state default; + } // no_sensor + +} // state Rerail + +//==== Locomotive-Specific Values & Code ============= ====// +//===================================================================// + + +// [VRCLocomotiveOpensourceScript] \ No newline at end of file diff --git a/Opensim Train/Train/Opensim Train/Opensim Train.prj b/Opensim Train/Train/Opensim Train/Opensim Train.prj new file mode 100644 index 00000000..18b9ccc6 --- /dev/null +++ b/Opensim Train/Train/Opensim Train/Opensim Train.prj @@ -0,0 +1,8 @@ + + + + + + + diff --git a/Opensim Train/Train/Train.sol b/Opensim Train/Train/Train.sol new file mode 100644 index 00000000..9ef62fe6 --- /dev/null +++ b/Opensim Train/Train/Train.sol @@ -0,0 +1,3 @@ + + + diff --git a/Opensim VIsitors List/Opensim VIsitors List/Object/Script.lsl b/Opensim VIsitors List/Opensim VIsitors List/Object/Script.lsl index e97d6308..3192731e 100644 --- a/Opensim VIsitors List/Opensim VIsitors List/Object/Script.lsl +++ b/Opensim VIsitors List/Opensim VIsitors List/Object/Script.lsl @@ -1,3 +1,14 @@ +// :SHOW: +// :CATEGORY:Meter +// :NAME:Visitor Display +// :AUTHOR:Unknown +// :KEYWORDS:Visitor Display +// :REV:1 +// :WORLD:Opensim +// :DESCRIPTION: +// Visitor Display using OsDrawText +// :CODE: + list gDetected = []; list gVisitors = []; @@ -6,112 +17,112 @@ string gTime = ""; display() { - string body = "width:512,height:512,aplha:FALSE,bgcolour:black"; - string draw = ""; - string log = "";//~ visitatori ~"; - string ad0 = " * Welcome to My World * \n"; - string ad1 = ""; - string ad2 = ""; - - integer visitors = llGetListLength(gVisitors) / 3; - - while(visitors--) - { - log = log + "\n" - + llList2String(gVisitors, visitors * 3 + 2) + " - " - + llList2String(gVisitors, visitors * 3 + 1) + " - " - + llList2String(gVisitors, visitors * 3); - } - - draw = osSetFontSize(draw, 12); - draw = osMovePen(draw, 16, 16); - draw = osSetPenColor(draw, "white"); - draw = osDrawText(draw, ad2 + "\n" + ad0 + "\n"+ ad1 + "\n" + log); - - osSetDynamicTextureDataBlendFace("", "vector", draw, body, FALSE, 2, 0, FALSE, 4); + string body = "width:512,height:512,aplha:FALSE,bgcolour:black"; + string draw = ""; + string log = "";//~ visitatori ~"; + string ad0 = " * Welcome to My World * \n"; + string ad1 = ""; + string ad2 = ""; + + integer visitors = llGetListLength(gVisitors) / 3; + + while(visitors--) + { + log = log + "\n" + + llList2String(gVisitors, visitors * 3 + 2) + " - " + + llList2String(gVisitors, visitors * 3 + 1) + " - " + + llList2String(gVisitors, visitors * 3); + } + + draw = osSetFontSize(draw, 12); + draw = osMovePen(draw, 16, 16); + draw = osSetPenColor(draw, "white"); + draw = osDrawText(draw, ad2 + "\n" + ad0 + "\n"+ ad1 + "\n" + log); + + osSetDynamicTextureDataBlendFace("", "vector", draw, body, FALSE, 2, 0, FALSE, 4); } - + string time() { //0123 4 56 7 89 0 12 3 45 - //YYYY - MM - DD T hh : mm:ss.ff..fZ - string now = llGetTimestamp(); - return llGetSubString(now,0,9) + " " + - llGetSubString(now,11,15); + //YYYY - MM - DD T hh : mm:ss.ff..fZ + string now = llGetTimestamp(); + return llGetSubString(now,0,9) + " " + + llGetSubString(now,11,15); } - + string duration(string timeIn, string timeOut) { - integer came = ((integer)llGetSubString(timeIn,11,12) * 60) + (integer)llGetSubString(timeIn,14,15); - integer went = ((integer)llGetSubString(timeOut,11,12) * 60) + (integer)llGetSubString(timeOut,14,15); - - if (came == went) if(llGetSubString(timeIn,8,9) != llGetSubString(timeOut,8,9)) went = went + 1440; - - if (came > went) went = went + 1440; - went = went - came; - - return llGetSubString("00" + (string)((went - (went % 60)) / 60), -2, -1) + ":" + - llGetSubString("00" + (string)(went % 60), -2, -1); + integer came = ((integer)llGetSubString(timeIn,11,12) * 60) + (integer)llGetSubString(timeIn,14,15); + integer went = ((integer)llGetSubString(timeOut,11,12) * 60) + (integer)llGetSubString(timeOut,14,15); + + if (came == went) if(llGetSubString(timeIn,8,9) != llGetSubString(timeOut,8,9)) went = went + 1440; + + if (came > went) went = went + 1440; + went = went - came; + + return llGetSubString("00" + (string)((went - (went % 60)) / 60), -2, -1) + ":" + + llGetSubString("00" + (string)(went % 60), -2, -1); } - + detectVisitorInOut(list avatars) { - integer avatar = llGetListLength(avatars); - string name = ""; - - while(avatar--) - { - name = llList2String(avatars, avatar); - if (llSubStringIndex((string)gDetected, name) == -1) gVisitors = gVisitors + [name, "00:00", gTime]; - - if (llGetListLength(gVisitors) >= 63) gVisitors = llDeleteSubList(gVisitors, 0, 2); - } - - avatar = llGetListLength(gDetected); - name = ""; - - while(avatar--) - { - name = llList2String(gDetected, avatar); - if (llSubStringIndex((string)avatars, name) == -1) - { - integer position = llListFindList(gVisitors, [name, "00:00"]) + 1; - string time = duration(llList2String(gVisitors, position + 1), gTime); - gVisitors = llListReplaceList(gVisitors, [time], position, position); - } - } + integer avatar = llGetListLength(avatars); + string name = ""; + + while(avatar--) + { + name = llList2String(avatars, avatar); + if (llSubStringIndex((string)gDetected, name) == -1) gVisitors = gVisitors + [name, "00:00", gTime]; + + if (llGetListLength(gVisitors) >= 63) gVisitors = llDeleteSubList(gVisitors, 0, 2); + } + + avatar = llGetListLength(gDetected); + name = ""; + + while(avatar--) + { + name = llList2String(gDetected, avatar); + if (llSubStringIndex((string)avatars, name) == -1) + { + integer position = llListFindList(gVisitors, [name, "00:00"]) + 1; + string time = duration(llList2String(gVisitors, position + 1), gTime); + gVisitors = llListReplaceList(gVisitors, [time], position, position); + } + } } - + visitorOut() { - integer avatar = llGetListLength(gDetected); - string name = ""; - - while(avatar--) - { - name = llList2String(gDetected, avatar); - integer position = llListFindList(gVisitors, [name, "00:00"]) + 1; - string time = duration(llList2String(gVisitors, position + 1), gTime); - gVisitors = llListReplaceList(gVisitors, [time], position, position); - } + integer avatar = llGetListLength(gDetected); + string name = ""; + + while(avatar--) + { + name = llList2String(gDetected, avatar); + integer position = llListFindList(gVisitors, [name, "00:00"]) + 1; + string time = duration(llList2String(gVisitors, position + 1), gTime); + gVisitors = llListReplaceList(gVisitors, [time], position, position); + } } - + default { - state_entry() { llSetTimerEvent(10.0); } + state_entry() { llSetTimerEvent(10.0); } - timer() - { - list avatarList = osGetAgents(); + timer() + { + list avatarList = osGetAgents(); - if ((string)avatarList != (string)gDetected) - { - gTime = time(); + if ((string)avatarList != (string)gDetected) + { + gTime = time(); - if (avatarList != []) detectVisitorInOut(avatarList); - else visitorOut(); + if (avatarList != []) detectVisitorInOut(avatarList); + else visitorOut(); - gDetected = avatarList; - display(); - } - } + gDetected = avatarList; + display(); + } + } } \ No newline at end of file diff --git a/Paramour Traffic Monitor/Paramour Traffic Monitor.sol b/Paramour Traffic Monitor/Paramour Traffic Monitor.sol new file mode 100644 index 00000000..92cccdf8 --- /dev/null +++ b/Paramour Traffic Monitor/Paramour Traffic Monitor.sol @@ -0,0 +1,3 @@ + + + diff --git a/Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Notecard.txt b/Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Notecard.txt new file mode 100644 index 00000000..48a05a98 --- /dev/null +++ b/Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Notecard.txt @@ -0,0 +1,60 @@ +// :SHOW: +// :CATEGORY:Region Traffic Monitor +// :NAME:Region Traffic Monitor Stats Retriever +// :AUTHOR:Aine Caoimhe +// :KEYWORDS:Region Traffic Monitor +// :REV:2.0 +// :WORLD:Opensim, Second Life +// :DESCRIPTION: +// Paramour Stats Retriever +// :CODE: + + + +PARAMOUR REGION TRAFFIC MONITOR AND STATS RETRIEVER v2.0 +by Aine Caoimhe (LACM) Sept. 2015 + +This is a region traffic monitor and greeter script and companion detailed stats retriever intended for high volume regions where the greeter needs to have the minimum possible impact on the region, particularly at peak traffic times. + +I accomplished this by streamlining many of the functions in the basic script v1.0 that I released 2+ years ago, and by altering the storage mechanism somewhat to both spread out the "hit" and to allow it to be used in a very high traffic region where the total visitor count could exceed the ~750 record storage capability limit of a notecard. Now, each month's traffic data is held on a separate notecard and, for further efficiency, tracking is done using UNIC timestamps. Since those aren't particularly human-friendly to read, I've added an optional "reporting" module (the Stats Retriever) that will parse and convert a selected month's data into a list showing day of the week, date and time of each person's visit as well as which grid they call home. There is a summary of that month's traffic supplied at the top. This is handed to the owner as a separate notecard that they can archive or use for whatever other purpose they wish. + +I removed the ability to give Notecards and/or Landmarks as part of the greeter's functions because doing so invokes a rather nasty 2-second thread-locking function that is utterly unsuitable for high traffic regions. Instead, I suggest you place a separate box of these nearby and set it to sell the contents to someone. This transfers the contents without locking up precious script threads. Your greeting message can inform the visitor where to pick those up. + +This script is a region-wide one (it detects and greets anyone entering the region, not just the parcel, and there is no range limit (even on huge vars!). It does require the use of OSSL functions to achieve this (and to store its data) + +You can use the box this comes in as your greeter and put it anywhere in the region you like; or you can pull the scripts out and place them in any other object you like. + +INSTRUCTIONS + +ENSURE OSSL FUNCTIONS ARE ENABLED IN THE REGION +The scripts use the following OSSL functions: +osGetAvatarList() used to poll who is in the region +osGetNotecard() to retrive stored data +osMakeNotecard() to presist data reliably +osGetGridName() to find out what grid this script is running in +osIsNpc() to avoid greeting NPCs + +Details for how to enable OSSL may be found at www.Opensimulator.org + +REZ OR INSERT THE SCRIPTS +Either rez the greeter box and use it as your greeter (you'll probably want to change the box's name) or take a copy of the scripts (and this readme notecard) and place them in any existing object in the region to have it act as your greeter. + +The "Stats Retriever" script it optional and if you're content with the basic "floaty stats" report above the greeter you can omit it entirely. You can also add or remove it later since it does all of its work on touch and is otherwise dormant. + +CONFIGURE THE SCRIPTS +Both scripts are DISABLED BY DEFAULT to allow you to do the initial configuration settings. Then, once you're ready to use it, simply enable them (see below). + +3.1 Configuring the main Region Traffic Monitor script +The top section of the script contains a variety of customizable settings for the monitor that you might wish to adjust. Instructions for each one are contained in the body of the script. +Once you're satisfied with the settings, change line 39 to read: +integer enabled=TRUE; +then save. This will enable the monitor and it will begin to do its thing. + +3.2 Configuring the Stats Retriever script +There are no user settings for this script other than to enable or disable it. Once you're ready to use it simply change line 15 to read: +integer enabled=TRUE; +then save. You will then be able to touch the greeter to obtain the reports. Only the owner can do so. + +LICENSE +Both scripts are provided under Creative Commons Attribution-Non-Commercial-ShareAlike 4.0 International license. Please be sure you read and adhere to the terms of this license: https://creativecommons.org/licenses/by-nc-sa/4.0/ +You can further customize or modify either to meet your own needs. \ No newline at end of file diff --git a/Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Paramour+Region+Traffic+Monitor+v2.0.lsl b/Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Paramour+Region+Traffic+Monitor+v2.0.lsl new file mode 100644 index 00000000..f33ae547 --- /dev/null +++ b/Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Paramour+Region+Traffic+Monitor+v2.0.lsl @@ -0,0 +1,348 @@ +// :SHOW: +// :CATEGORY:Region Traffic Monitor +// :NAME:Region Traffic Monitor Stats Retriever +// :AUTHOR:Aine Caoimhe +// :KEYWORDS:Region Traffic Monitor +// :REV:2.0 +// :WORLD:Opensim, Second Life +// :DESCRIPTION: +// Paramour Stats Retriever +// :CODE: + + +// Paramour Region Traffic Monitor v2.0 +// By Aine Caoimhe (LACM) Sept 2015 +// Provided under Creative Commons Attribution-Non-Commercial-ShareAlike 4.0 International license. +// Please be sure you read and adhere to the terms of this license: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// +// This version of the region greeter is designed to handle potentially extremely high traffic areas where the number of +// unique visitors can exceed the store limits of a single notecard. I have also altered the store logic to make it more +// suitable for high traffic locations as well (much lower load). This new version will still fail if you have more than +// approximately 750 unique visitors in a single month (notecards can hold only 64KB of data and any attempt to write +// more than this fails will result in a blank notecard without generating any error/warning in world or in console). +// The exact failure point depends on the lengths of each visitor's name so it could conceivably fail with fewer than +// this, or succeed with even 1000 unique visitors...it all depends on the total character count. +// +// This version DOES NOT include option to give landmarks or notecards to new visitors since this invokes a rather nasty +// 3-second thread-lock I consider inappropriate for the kind of region this is intended to serve. Instead, I suggest +// putting a separate box nearby that contains those items and is set to touch-to-buy-contents (which results in the +// same thing for the visitor but without the painful thread-lock). +// +// The script tracks user visits by UNIX time (UTC) and uses it for internal calculations so roll-over to a new day +// occurs at midnight UTC in all storage and reports. Someone who visits multiple times in a single month will only +// be listed once in that month's log, using the date/time of their last visit during that period +// +// There is an option to display stats as floaty text above the prim containing the script. These stats update each time +// someone new enters the region, or hourly if there have been no changes during the previous 60 minutes +// +// Set the paramters as desired in the User Settings section below or leave them at their suggested defaults if you wish. +// +// **** REQUIRES OSSL ENABLED IN THE REGION AND SUFFICIENT PERMISSIONS FOR THE SCRIPT OWNER **** +// Uses the following OSSL functions: +// - osGetAvatarList() used to poll who is in the region +// - osGetNotecard() to retrive stored data +// - osMakeNotecard() to presist data reliably +// - osGetGridName() to find out what grid this script is running in +// - osIsNpc() to avoid greeting NPCs +// +// * * * * * * * * * * * * +// * * USER SETTINGS * * +// * * * * * * * * * * * * +// +integer enabled=FALSE; +// TRUE = this script is enabled.....FALSE = this script is disabled and you can make other adjustments to the settings without it going through its normal routines +// Set it to TRUE to start using the script +// +// Set the following according to your preferences +float pollRate=30.0; +// how often (in seconds) to check for a new visitor...shorter values induce higher sim load +// +float persistDelay=300.0; +// when there is new data to persist, how long to wait (in seconds) before writing the notecard. If someone else arrives during this time +// the persist timer is reset. This ensures that the action of storing the new notecard doesn't contribute to sim lag during the initial +// heavy hit of a login. Keep in mind when shutting down a +// +string firstGreetMessage="Welcome, $name. Thank you for visiting my region."; +// This message will be shown to the guest only on their very first visit. Use the special wildcard string "$name" which will be replaced +// by the person's actual name when they are greeted. Replace this generic text with anything you like. +// +string repeatGreetMessage="Welcome back, $name. Please enjoy your stay."; +// This message will be shown on all subsequent visits by a guest. Again, $name will be replaced by the guest's actual name. +// +integer notifyOwner=TRUE; +// TRUE = when someone new enters the region tell the owner who arrived (using region chat if possible) +/// FALSE = don't notify the owner +// +// IMPORTANT!!!!!! DO NOT ENABLE THIS NEXT FEATURE IN ANY HIGH TRAFFIC REGION UNLESS YOU WILL ALWAYS BE PRESENT IN THE REGION AT HIGH TRAFFIC TIMES!!!!!!! +// The setting above only notifies you when you're in the region. To be notified when you're in another region requires enabling the next option which will instant-message you +// but the function used to send the message requires the use of a VERY NASTY 2-second threadlock function which can result in severe reduction to the +// performance of the simulator if used too frequently. You can easily use the reporting functions to find out who has visited the region so only enable this if you absolutely need +// to be told immediately when someone arrives, and you should go to that region as soon as possible and remain there if you expect more visitors since each +// new arrival will apply this same hit. I only included it as an option at all due to popular request and I won't be sympathetic if its use lags or even crashes your simulator. +integer messageOwner=FALSE; +// IGNORED if notifyOwner is set to FALSE +// TRUE = if the owner is not in the region, send an instant message to the owner to notify then when someone enters. NOT RECOMMENDED!!!!!!! +// FALSE = don't message the owner +// +integer showFloatyStats=TRUE; +vector floatyColour=<0.0, 1.0, 0.0>; +float floatyAlpha=1.0; +// showFloatyStats = TRUE - the sim visitor stats will be shown as floaty text above the prim that contains the script +// showFloatyStats = FALSE - no floaty text stats +// If you turn on floaty stats, the floatyColour and floatyAlpha control the colour and transparancy of the text using the +// standard LSL RGB colour vector (<0,0,0> = black, <1,1,1> = white) and alpha (0.0=invisible, 1.0 = opaque). If off, they're ignored +// +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// DON'T CHANGE ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU'RE DOING +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// +integer thisMonth; +integer thisYear; +list monthList; // UTC | Name | grid | key +list masterList; // UTC | Key +list greetedList; // key +integer lastStatsUpdate; +string thisGrid; + +doFloatyUpdate() +{ + lastStatsUpdate=llGetUnixTime(); + if (!showFloatyStats) return; + string stats=llGetRegionName()+" visitor stats\n "; + integer visDay; + integer day=24*60*60; // how many seconds in a day + integer visWeek; + integer week=day*7; // how many seconds in a week + integer visMonth; + integer month=day*30; // how many seconds in 30 days + integer i; + integer l=llGetListLength(masterList); + while (imonth) i=l; + else i+=2; + } + stats+="\nIn Region Now: "+(string)llGetListLength(greetedList); + stats+="\nLast 24 hours: "+(string)visDay; + stats+="\nLast 7 days: "+(string)visWeek; + stats+="\nLast 30 days: "+(string)visMonth; + stats+="\nAll Time: "+(string)(l/2); + stats+="\n(all counts are unique visitors)"; + llSetText(stats,floatyColour,floatyAlpha); +} +integer updatePresence() +{ + // returns TRUE if someone new has arrived in the region, else FALSE + integer isChanged=FALSE; + integer ownerPresent=FALSE; + if (llGetAgentSize(llGetOwner())!=ZERO_VECTOR) ownerPresent=TRUE; + integer now=llGetUnixTime(); + list inRegion=osGetAvatarList(); // key, position, name + if (llGetAgentSize(llGetOwner())!=ZERO_VECTOR) inRegion=[]+inRegion+[llGetOwner(),ZERO_VECTOR]+llGetObjectDetails(llGetOwner(),[OBJECT_NAME]); + integer i; + integer l=llGetListLength(inRegion); + while (i-1) + { + // this person is from another grid so extract that and trim it from the name + grid =llGetSubString(name,sep+2,-1); + name=""+llGetSubString(name,0,sep-1); + // a hypergrid name is also using a dot as a separator so fix that + sep=llSubStringIndex(name,"."); + name=""+llGetSubString(name,0,sep-1)+" "+llGetSubString(name,sep+1,-1); + } + // grid and name are now correct so find out if this is a new visitor and greet them + string greeting=repeatGreetMessage; // default to this + integer visit=llListFindList(masterList,[who]); + if (visit==-1) + { + // first time visitor we need to add to both master list and monthly list + greeting=""+firstGreetMessage; // switch to first time greeting message instead + masterList=[]+masterList+[now,who]; + monthList=[]+monthList+[now,name,grid,who]; + } + else + { + // return visitor...update master first + masterList=[]+llListReplaceList(masterList,[now],visit-1,visit-1); + // see if they're also already in this month's list + integer monIndex=llListFindList(monthList,[who]); + if (monIndex==-1) monthList=[]+monthList+[now,name,grid,who]; // add new to list + else monthList=[]+llListReplaceList(monthList,[now],monIndex-3,monIndex-3); // update return to list + } + // greet the visitor but first subsitute $name with user's name + integer swapInd=llSubStringIndex(greeting,"$name"); + while (swapInd>-1) + { + greeting=""+llGetSubString(greeting,0,swapInd-1)+name+llGetSubString(greeting,swapInd+5,-1); + swapInd=llSubStringIndex(greeting,"$name"); + } + llRegionSayTo(who,0,greeting); + // notify owner + if (notifyOwner) + { + if(ownerPresent) llRegionSayTo(llGetOwner(),0,name+" entered the region"); + else if (messageOwner) llInstantMessage(llGetOwner(),name+" entered your "+llGetRegionName()+" region"); + } + // add to the greeted list + greetedList=[]+greetedList+[who]; + } + } + i+=3; // next person + } + // update greetedList to reflect anyone who has left + l=llGetListLength(greetedList); + while (--l>=0) + { + if(llListFindList(inRegion,[llList2Key(greetedList,l)])==-1) greetedList=[]+llDeleteSubList(greetedList,l,l); + } + // if there have been any changes we need to do a few further things + if (isChanged) + { + // sort both lists in descending order + masterList=[]+llListSort(masterList,2,FALSE); + monthList=[]+llListSort(monthList,4,FALSE); + llSensorRepeat("THIS_SHOULD_NEVER_EVER_RETURN_A_SENSOR_RESULT",NULL_KEY,SCRIPTED,0.1,PI,persistDelay); + return TRUE; + } + else return FALSE; +} +persistData() +{ + // whenever data has changed and new persist needs to happen + // clear sensor if there is one + llSensorRemove(); + string cardName="log: "+(string)thisYear+"-"; + if (thisMonth<10) cardName+="0"; + cardName+=(string)thisMonth; + if (llGetInventoryType(cardName)==INVENTORY_NOTECARD) + { + llRemoveInventory(cardName); + llSleep(0.25); // have to sleep to give it time to register + } + integer i; + integer l=llGetListLength(monthList); + string data; + while (i0) osMakeNotecard(cardName,data); +} +doMonthRollOver(list today) +{ + // triggered by first tick of the timer after month changes so force a persist to close out previous month first + persistData(); + // set new date + thisYear=llList2Integer(today,0); + thisMonth=llList2Integer(today,1); + // put anyone currently in the region into the new month list - build it using previous month's list then replace + list newMonthList=[]; + integer now=llGetUnixTime(); + integer i=llGetListLength(greetedList); + while (--i>-1) + { + integer index=llListFindList(monthList,[llList2Key(greetedList,i)]); + newMonthList=[]+newMonthList+[now]+llList2List(monthList,index-2,index); + } + monthList=[]+newMonthList; + // need to force persist of this new month too + persistData(); +} +loadData() +{ + monthList=[]; + masterList=[]; + greetedList=[]; + integer i=llGetInventoryNumber(INVENTORY_NOTECARD); + while (--i>-1) + { + string card=llGetInventoryName(INVENTORY_NOTECARD,i); + if (llSubStringIndex(card,"log: ")==0) + { + integer year=(integer)llGetSubString(card,5,8); + integer month=(integer)llGetSubString(card,10,11); + list thisData=llParseString2List(osGetNotecard(card),["|","\n"],[]); + if ((year==thisYear) && (month==thisMonth)) monthList=[]+thisData; + integer e; + integer l=llGetListLength(thisData); + while (e 3600) doFloatyUpdate(); + } +} \ No newline at end of file diff --git a/Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Paramour+Stats+Retriever+for+Region+Traffic+Monitor+2.0 Script 2.lsl b/Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Paramour+Stats+Retriever+for+Region+Traffic+Monitor+2.0 Script 2.lsl new file mode 100644 index 00000000..14347241 --- /dev/null +++ b/Paramour Traffic Monitor/Paramour Traffic Monitor/Object/Paramour+Stats+Retriever+for+Region+Traffic+Monitor+2.0 Script 2.lsl @@ -0,0 +1,247 @@ +// :SHOW: +// :CATEGORY:Region Traffic Monitor +// :NAME:Region Traffic Monitor Stats Retriever +// :AUTHOR:Aine Caoimhe +// :KEYWORDS:Region Traffic Monitor +// :REV:2.0 +// :WORLD:Opensim, Second Life +// :DESCRIPTION: +// Paramour Stats Retriever +// :CODE: + +// Paramour Stats Retriever for Region Traffic Monitor 2.0 +// by Aine Caoimhe (LACM) Setp 2015 +// Provided under Creative Commons Attribution-Non-Commercial-ShareAlike 4.0 International license. +// Please be sure you read and adhere to the terms of this license: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// +// Requires no additional OSSL functions other than the ones that already need to be enabled for the Traffic Monitor +// +// Simply Place this script in the same prim as the Traffic Monitor script (and log notecards) then touch +// Reports are handed to you directly (and very briefly stored in the prim in order to be able to deliver it to you, then deleted) +// Times/dates are converted to SL time and is daylight-savings aware +// original dates/times stored on source cards are UTC so may overlap to next month on final day +// If the floaty text report is sufficient you don't need to put this into the prim at all. +// Don't touch to use this during peak traffic periods as it does invoke a thread-locking function. +// +// There is only 1 user settings for this script to enable or disable it +integer enabled=FALSE; +// FALSE = disable the script....TRUE = the script is enabled +// +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// DO NOT CHANGE ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU'RE DOING +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// +float timeOut=120.0; // 2 minutes to respond to a dialog or remove listener +integer myChannel; +integer handle; +string txtDia; +list butDia; +list logs; +integer indLog; +string thisGrid; +list months=["months","January","February","March","April","May","June","July","August","September","October","November","December"]; + +// ********************** THIS PART COMES FROM SECOND LIFE WIKI: http://wiki.secondlife.com/wiki/Unix2PST_PDT **************** +// Convert Unix Time to SLT, identifying whether it is currently PST or PDT (i.e. Daylight Saving aware) +// Original script: Omei Qunhua December 2013 +// Fixed by Aine Caoimhe to work in Opensim Setp 2015 +// Returns a string containing the SLT date and time, annotated with PST or PDT as appropriate, corresponding to the given Unix time. +// e.g. Wed 2013-12-25 06:48 PST +// +list weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + +string Unix2PST_PDT(integer insecs) +{ + string str = Convert(insecs - (3600 * 8) ); // PST is 8 hours behind GMT + if (llGetSubString(str, -3, -1) == "PDT") // if the result indicates Daylight Saving Time ... + str = Convert(insecs - (3600 * 7) ); // ... Recompute at 1 hour later + return str; +} + +// This leap year test is correct for all years from 1901 to 2099 and hence is quite adequate for Unix Time computations +integer LeapYear(integer year) +{ + return !(year & 3); +} + +integer DaysPerMonth(integer year, integer month) +{ + if (month == 2) return 28 + LeapYear(year); + return 30 + ( (month + (month > 7) ) & 1); // Odd months up to July, and even months after July, have 31 days +} + +string Convert(integer insecs) +{ + integer w; integer month; integer daysinyear; + integer mins = insecs / 60; + integer secs = insecs % 60; + integer hours = mins / 60; + mins = mins % 60; + integer days = hours / 24; + hours = hours % 24; + integer DayOfWeek = (days + 4) % 7; // 0=Sun thru 6=Sat + + integer years = 1970 + 4 * (days / 1461); + days = days % 1461; // number of days into a 4-year cycle + + @loop; + daysinyear = 365 + LeapYear(years); + if (days >= daysinyear) + { + days -= daysinyear; + ++years; + jump loop; + } + ++days; + + month=0; + w=0; + while (days > w) + { + days -= w; + w = DaysPerMonth(years, ++month); + } + string str = ((string) years + "-" + llGetSubString ("0" + (string) month, -2, -1) + "-" + llGetSubString ("0" + (string) days, -2, -1) + " " + + llGetSubString ("0" + (string) hours, -2, -1) + ":" + llGetSubString ("0" + (string) mins, -2, -1) ); + + integer LastSunday = days - DayOfWeek; + string PST_PDT = " PST"; // start by assuming Pacific Standard Time + // Up to 2006, PDT is from the first Sunday in April to the last Sunday in October + // After 2006, PDT is from the 2nd Sunday in March to the first Sunday in November + if (years > 2006 && month == 3 && LastSunday > 7) PST_PDT = " PDT"; + if (month > 3) PST_PDT = " PDT"; + if (month > 10) PST_PDT = " PST"; + if (years < 2007 && month == 10 && LastSunday > 24) PST_PDT = " PST"; + return (llList2String(weekdays, DayOfWeek) + " " + str + PST_PDT); +} +// ***************** END OF FUNCTION FROM SL WIKI **************************** + +doFetch(string cardName) +{ + integer year=(integer)llGetSubString(cardName,5,8); + integer month=(integer)llGetSubString(cardName,10,11); + list data=llParseString2List(osGetNotecard(cardName),["|","\n"],[]); + string details; + list grids; + integer visCount; + integer localCount; + integer hgCount; + integer i; + integer l=llGetListLength(data); + while (i"]; + startListening(); +} +doPrebuild() +{ + integer i=llGetInventoryNumber(INVENTORY_NOTECARD); + logs=[]; + indLog=0; + while (--i>=0) + { + string name=llGetInventoryName(INVENTORY_NOTECARD,i); + if (llSubStringIndex(name,"log: ")==0) logs=[]+logs+[llGetSubString(name,5,-1)]; + } +} +startListening() +{ + llSetTimerEvent(timeOut); + handle=llListen(myChannel,"",llGetOwner(),""); + llDialog(llGetOwner(),txtDia,llList2List(butDia,9,11)+llList2List(butDia,6,8)+llList2List(butDia,3,5)+llList2List(butDia,0,2),myChannel); +} +stopListening() +{ + llSetTimerEvent(0.0); + llListenRemove(handle); +} + +default +{ + state_entry() + { + thisGrid=osGetGridName(); + myChannel=0x80000000|(integer)("0x"+(string)llGetKey()); + } + changed (integer change) + { + if (change & CHANGED_OWNER) llResetScript(); + else if (change & CHANGED_REGION_START) llResetScript(); + } + on_rez(integer foo) + { + llResetScript(); + } + touch_start(integer num) + { + if (llDetectedKey(0)!=llGetOwner()) return; // owner only can retrieve stats + if (enabled) + { + doPrebuild(); + showMenuMain(); + } + else llOwnerSay("The stats retriever script is currently set to disabled. To enable to change line 15 to enabled=TRUE and save"); + } + timer() + { + stopListening(); + } + listen(integer channel, string name, key who, string message) + { + stopListening(); + if (message=="DONE") return; + else if (message=="-") startListening(); + else if ((message=="< PREV") || (message=="NEXT >")) + { + if (message=="NEXT >") indLog+=9; + else indLog-=9; + if (indLog>=llGetListLength(logs)) indLog=0; + if (indLog<=-9) indLog=llGetListLength(logs)-9; + if (indLog<0) indLog=0; + showMenuMain(); + } + else + { + string logToFetch="log: "+message; + if (llGetInventoryType(logToFetch)!=INVENTORY_NOTECARD) + { + llOwnerSay("ERROR: could not find that card....rebuilding card list"); + doPrebuild(); + showMenuMain(); + } + else doFetch(logToFetch); + startListening(); + } + } + +} \ No newline at end of file diff --git a/Paramour Traffic Monitor/Paramour Traffic Monitor/Paramour Traffic Monitor.prj b/Paramour Traffic Monitor/Paramour Traffic Monitor/Paramour Traffic Monitor.prj new file mode 100644 index 00000000..e3da0461 --- /dev/null +++ b/Paramour Traffic Monitor/Paramour Traffic Monitor/Paramour Traffic Monitor.prj @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/Trash Collector NPC/Trash Collector NPC/NPC Box/Trash Bot b/Trash Collector NPC/Trash Collector NPC/NPC Box/Trash Bot new file mode 100644 index 00000000..bc9432fc --- /dev/null +++ b/Trash Collector NPC/Trash Collector NPC/NPC Box/Trash Bot @@ -0,0 +1 @@ +Put a NPC appearance notecard here named "Trash Bot" \ No newline at end of file diff --git a/Trash Collector NPC/Trash Collector NPC/NPC Box/Trash Collector script.lsl b/Trash Collector NPC/Trash Collector NPC/NPC Box/Trash Collector script.lsl index e41d6de2..4860c2cd 100644 --- a/Trash Collector NPC/Trash Collector NPC/NPC Box/Trash Collector script.lsl +++ b/Trash Collector NPC/Trash Collector NPC/NPC Box/Trash Collector script.lsl @@ -1,12 +1,22 @@ -//:AUTHOR:Unknown -//:Name:Trash Collector NPC script - -// comment out for runtime, for LSLEditor use +// :SHOW: +// :CATEGORY:Presentation +// :NAME:Trash Collector +// :AUTHOR:Unknown +// :KEYWORDS: NPC +// :REV:1 +// :WORLD:Opensim +// :DESCRIPTION: +// NPC that walks and collects trash +// :CODE: +// You newed a animation fort your NPC to bend overt and pick stuff up. +string pickup="pick up from ground animation"; +// comment this line out for runtime, for LSLEditor use integer OS_NPC_NO_FLY = 1; +float WAIT = 10; // wait 10 secobnds +float range = 15; // how far to wander +vector start_location = <0,0,0>; -float WAIT = 10; -float range = 15; -vector start_location = <355.80, 326.80, 44>; +//CODDE // define a box in front of the NPC size 'range' vector top; @@ -25,156 +35,154 @@ integer debug = TRUE; DEBUG(string str) { - if (debug) llSay(0,str); + if (debug) llSay(0,str); } -string pickup="pick up from ground animation"; - TimerEvent(float timesent) { - DEBUG("Setting timer: " + (string) timesent); - llSetTimerEvent(timesent); + DEBUG("Setting timer: " + (string) timesent); + llSetTimerEvent(timesent); } create_path(vector start,vector goal) { - vector pos = llGetPos(); - integer direct; - list results = llCastRay(start, goal, [ RC_MAX_HITS, 1] ); - direct=llList2Integer(results, -1); - if (!direct) - { - positions = [start, goal]; - return; - } - if (pos.y < goal.y) - positions= [start,top, goal]; - else if (goal.x < pos.x) - positions= [start,top,left,goal]; - else if (goal.x > pos.x) - positions= [start,top,right,goal]; - return; + vector pos = llGetPos(); + integer direct; + list results = llCastRay(start, goal, [ RC_MAX_HITS, 1] ); + direct=llList2Integer(results, -1); + if (!direct) + { + positions = [start, goal]; + return; + } + if (pos.y < goal.y) + positions= [start,top, goal]; + else if (goal.x < pos.x) + positions= [start,top,left,goal]; + else if (goal.x > pos.x) + positions= [start,top,right,goal]; + return; } send_path(integer homewardbound, list path) { - integer length=llGetListLength(path); + integer length=llGetListLength(path); - if(destination < length && destination >= -length) - { - DEBUG((string) destination); - newDest = llList2Vector(path,destination); - iWaitCounter = WAIT; // wait to get to a destination. - osNpcMoveToTarget(NPCKey, newDest, OS_NPC_NO_FLY ); - TimerEvent(1); - return; - } else if (destination == length) { - osNpcPlayAnimation(NPCKey,pickup); - TimerEvent(3.0); - } else if (destination <= -length) { - osNpcRemove(NPCKey); - TimerEvent(480); - checkAV=TRUE; - return; - } + if(destination < length && destination >= -length) + { + DEBUG((string) destination); + newDest = llList2Vector(path,destination); + iWaitCounter = WAIT; // wait to get to a destination. + osNpcMoveToTarget(NPCKey, newDest, OS_NPC_NO_FLY ); + TimerEvent(1); + return; + } else if (destination == length) { + osNpcPlayAnimation(NPCKey,pickup); + TimerEvent(3.0); + } else if (destination <= -length) { + osNpcRemove(NPCKey); + TimerEvent(480); + checkAV=TRUE; + return; + } - destination= -1; - TimerEvent(1); - iWaitCounter = WAIT; - DEBUG((string)destination); + destination= -1; + TimerEvent(1); + iWaitCounter = WAIT; + DEBUG((string)destination); } init() { - vector pos = llGetPos(); - top = pos + <0,range,0>; - right = pos + ; - left = pos - ; - destination=0; - TimerEvent(480); // 8 minutes !!! - checkAV=FALSE; - destination=0; - create_path(start_location,randompos()); - DEBUG((string)positions); - NPCKey=osNpcCreate("Trash", "Bot", start_location, "Trash Bot"); - send_path(TRUE, positions); + vector pos = llGetPos(); + top = pos + <0,range,0>; + right = pos + ; + left = pos - ; + destination=0; + TimerEvent(480); // 8 minutes !!! + checkAV=FALSE; + destination=0; + create_path(llGetPos() + start_location,randompos()); + DEBUG((string)positions); + NPCKey=osNpcCreate("Trash", "Bot", start_location, "Trash Bot"); + send_path(TRUE, positions); } vector randompos() { - vector pos= llGetPos(); - if(llFrand(1)<0.5) - { - float randx= 18*((llRound(llFrand(1))*2)-1); - //llOwnerSay((string)randx); - float randy=18*((llRound(llFrand(1))*2)-1); - //llOwnerSay((string)randy); - return pos+; - } - else - { - return pos+; - } + vector pos= llGetPos(); + if(llFrand(1)<0.5) + { + float randx= 18*((llRound(llFrand(1))*2)-1); + //llOwnerSay((string)randx); + float randy=18*((llRound(llFrand(1))*2)-1); + //llOwnerSay((string)randy); + return pos+; + } + else + { + return pos+; + } } check_agents() { - integer nNew = 0; - list avis = llGetAgentList(AGENT_LIST_REGION, []); - integer howmany = llGetListLength(avis); - integer i; - for ( i = 0; i < howmany; i++ ) { - if ( ! osIsNpc(llList2Key(avis, i)) ) - nNew++; // only non-NPC's - } - if (nNew>0) - { - checkAV=FALSE; - destination=0; - create_path(start_location,randompos()); - DEBUG((string)positions); - NPCKey=osNpcCreate("Trash", "Bot", start_location, "Trash Bot"); - send_path(TRUE, positions); + integer nNew = 0; + list avis = llGetAgentList(AGENT_LIST_REGION, []); + integer howmany = llGetListLength(avis); + integer i; + for ( i = 0; i < howmany; i++ ) { + if ( ! osIsNpc(llList2Key(avis, i)) ) + nNew++; // only non-NPC's + } + if (nNew>0) + { + checkAV=FALSE; + destination=0; + create_path(start_location,randompos()); + DEBUG((string)positions); + NPCKey=osNpcCreate("Trash", "Bot", start_location, "Trash Bot"); + send_path(TRUE, positions); - } + } } default { - state_entry() - { - init(); - } - touch_start(integer total_number) - { - checkAV=FALSE; - destination=0; - create_path(start_location,randompos()); - //llOwnerSay((string)positions); - NPCKey=osNpcCreate("Trash", "Bot", start_location, "Trash Bot"); - send_path(TRUE, positions); - } - timer() { - if (checkAV) - { - check_agents(); - } - if (--iWaitCounter) { + state_entry() + { + init(); + } + touch_start(integer total_number) + { + checkAV=FALSE; + destination=0; + create_path(start_location,randompos()); + //llOwnerSay((string)positions); + NPCKey=osNpcCreate("Trash", "Bot", start_location, "Trash Bot"); + send_path(TRUE, positions); + } + timer() { + if (checkAV) + { + check_agents(); + } + if (--iWaitCounter) { - vector tDest = newDest; - tDest.z = 0; - vector hisDest = osNpcGetPos(NPCKey); - hisDest.z = 0; + vector tDest = newDest; + tDest.z = 0; + vector hisDest = osNpcGetPos(NPCKey); + hisDest.z = 0; - if (llVecDist(hisDest, newDest) > 1) { - return; - } - } - //llSetTimerEvent(0); - if (destination >= 0) - destination++; - else - destination--; - - send_path(TRUE, positions); - } + if (llVecDist(hisDest, newDest) > 1) { + return; + } + } + //llSetTimerEvent(0); + if (destination >= 0) + destination++; + else + destination--; + + send_path(TRUE, positions); + } } \ No newline at end of file diff --git a/Trash Collector NPC/Trash Collector NPC/Trash Collector NPC.prj b/Trash Collector NPC/Trash Collector NPC/Trash Collector NPC.prj index af945bf6..cf7e4f30 100644 --- a/Trash Collector NPC/Trash Collector NPC/Trash Collector NPC.prj +++ b/Trash Collector NPC/Trash Collector NPC/Trash Collector NPC.prj @@ -1,5 +1,7 @@ + +