diff --git a/res/sounds/README.md b/res/sounds/README.md index 613fe7944..600c276f8 100644 --- a/res/sounds/README.md +++ b/res/sounds/README.md @@ -17,3 +17,49 @@ Constraints specific to Second Life®: License: * All sounds and binaries contributed to this repository are licensed under a [Creative Commons Attribution-ShareAlike 4.0 International Public License](https://creativecommons.org/licenses/by-sa/4.0/) + + +## List new sounds +When adding new sounds. Make noise normalization, that they sound same volume. + +Audacity / Effecs / Volume and compression / Loudness Normalization -14 LUFS +Audacity / Effecs / Noise removal and repair / Noise reduction (optional) +Audacity / Effecs / Volume and compression / Amplify (optional) +Audacity / Effecs / Volume and compression / Limiter -1 (optional Show / Clipping in waveform) + +This work batch can be used to Youtube videos too. +Perfect video has following stats: Volume / Normalized 100% / 0 dB + +bell_brass_01.wav 9a1f0d2e-0f32-21f7-7d83-1aeb428ace4a +bell_brass_02.wav b6985bfd-72c0-5e19-5d83-88f6c9bbe151 +bell_cow_01.wav 5837c0d4-cdb5-21f5-8743-5801614383a8 +bell_cow_02.wav 86c7c7ec-1862-5328-f908-00d341e0a5d2 +bell_cow_03.wav effb7813-18a3-7237-dcf0-873713e4a545 +bell_plastic_01.wav f75aaed4-2415-d5a0-bd94-4bb32432b42f +bell_plastic_02.wav 3c7ccb39-9ca8-d0ce-3561-cae901e7ad06 +bell_silver_01.wav f8be56c4-28c8-3095-4b61-d425467eb2cb +bell_silver_02.wav e0b061c0-9edd-6a49-cdd6-a197ab439391 +bell_silver_03.wav 378e5546-955d-b04f-8994-c7b0763b8f68 +bell_supersoft_01.wav +bell_supersoft_02.wav +drop_808_0010.wav +lock_015_01.wav +lock_015_02.wav +lock_015_03.wav +lock_015_04.wav +lock_015_05.wav +lock_015_06.wav +morse_ask.wav +morse_do.wav +morse_do_ao.wav +morse_do_hud.wav + +## Old sounds +ae3a836f-4d69-2b74-1d52-9c78a9106206 ---- Humming noise, should be bell +503d2360-99f8-7a4a-8b89-43c5122927bd --- Little bling volume very low +a3ff9ca6-8289-0007-5b6b-d4c993580a6b -- brass bell volume low +843adc44-1189-2d67-6f3a-72a80b3a9ed4 -- cow bell +4c84b9b7-b363-b501-c019-8eef5fb4d3c2 -- cow bell2 +3b95831e-8da5-597f-3b4d-713a03945cb6 -- tiny cat bell +285b317c-23d1-de51-84bc-938eb3df9e46 -- tiny cat bell 2 +074b9b37-f6a3-a0a3-f40e-14bc57502435 -- xmas bell diff --git a/res/sounds/bell_brass_01.wav b/res/sounds/bell_brass_01.wav index 349d8b1d6..21e79391d 100644 Binary files a/res/sounds/bell_brass_01.wav and b/res/sounds/bell_brass_01.wav differ diff --git a/res/sounds/bell_brass_02.wav b/res/sounds/bell_brass_02.wav index 476029500..a9279caf2 100644 Binary files a/res/sounds/bell_brass_02.wav and b/res/sounds/bell_brass_02.wav differ diff --git a/res/sounds/bell_cow_01.wav b/res/sounds/bell_cow_01.wav index bb1944852..d97ab9492 100644 Binary files a/res/sounds/bell_cow_01.wav and b/res/sounds/bell_cow_01.wav differ diff --git a/res/sounds/bell_cow_02.wav b/res/sounds/bell_cow_02.wav index 8735f0f6b..372f38d4a 100644 Binary files a/res/sounds/bell_cow_02.wav and b/res/sounds/bell_cow_02.wav differ diff --git a/res/sounds/bell_cow_03.wav b/res/sounds/bell_cow_03.wav index 28f6c056c..4a761037d 100644 Binary files a/res/sounds/bell_cow_03.wav and b/res/sounds/bell_cow_03.wav differ diff --git a/res/sounds/bell_plastic_01.wav b/res/sounds/bell_plastic_01.wav index 2b7e067dd..3fd832321 100644 Binary files a/res/sounds/bell_plastic_01.wav and b/res/sounds/bell_plastic_01.wav differ diff --git a/res/sounds/bell_plastic_02.wav b/res/sounds/bell_plastic_02.wav index 4008d5fb1..b3f3a8eb4 100644 Binary files a/res/sounds/bell_plastic_02.wav and b/res/sounds/bell_plastic_02.wav differ diff --git a/res/sounds/bell_silver_01.wav b/res/sounds/bell_silver_01.wav index f5be422cd..2d8091068 100644 Binary files a/res/sounds/bell_silver_01.wav and b/res/sounds/bell_silver_01.wav differ diff --git a/res/sounds/bell_silver_02.wav b/res/sounds/bell_silver_02.wav index 9c0a65d5c..0c3c3147f 100644 Binary files a/res/sounds/bell_silver_02.wav and b/res/sounds/bell_silver_02.wav differ diff --git a/res/sounds/bell_silver_03.wav b/res/sounds/bell_silver_03.wav index d916dc209..0f873f757 100644 Binary files a/res/sounds/bell_silver_03.wav and b/res/sounds/bell_silver_03.wav differ diff --git a/res/sounds/bell_supersoft_01.wav b/res/sounds/bell_supersoft_01.wav index ee79ce6d0..7ab001b44 100644 Binary files a/res/sounds/bell_supersoft_01.wav and b/res/sounds/bell_supersoft_01.wav differ diff --git a/res/sounds/bell_supersoft_02.wav b/res/sounds/bell_supersoft_02.wav index 8aefed7ec..0671beb69 100644 Binary files a/res/sounds/bell_supersoft_02.wav and b/res/sounds/bell_supersoft_02.wav differ diff --git a/res/sounds/drop_808_0010.wav b/res/sounds/drop_808_0010.wav index 6a9211b20..d42568efe 100644 Binary files a/res/sounds/drop_808_0010.wav and b/res/sounds/drop_808_0010.wav differ diff --git a/res/sounds/lock_015_01.wav b/res/sounds/lock_015_01.wav index 0ee47d61d..440e763cc 100644 Binary files a/res/sounds/lock_015_01.wav and b/res/sounds/lock_015_01.wav differ diff --git a/res/sounds/lock_015_02.wav b/res/sounds/lock_015_02.wav index ec4a5b889..5ac9f675e 100644 Binary files a/res/sounds/lock_015_02.wav and b/res/sounds/lock_015_02.wav differ diff --git a/res/sounds/lock_015_03.wav b/res/sounds/lock_015_03.wav index c2ce8c992..b721fcda4 100644 Binary files a/res/sounds/lock_015_03.wav and b/res/sounds/lock_015_03.wav differ diff --git a/res/sounds/lock_015_04.wav b/res/sounds/lock_015_04.wav index ca399267f..b9573399b 100644 Binary files a/res/sounds/lock_015_04.wav and b/res/sounds/lock_015_04.wav differ diff --git a/res/sounds/lock_015_05.wav b/res/sounds/lock_015_05.wav index 3416f65e0..f14be16cb 100644 Binary files a/res/sounds/lock_015_05.wav and b/res/sounds/lock_015_05.wav differ diff --git a/res/sounds/lock_015_06.wav b/res/sounds/lock_015_06.wav index be2291f61..f79605c25 100644 Binary files a/res/sounds/lock_015_06.wav and b/res/sounds/lock_015_06.wav differ diff --git a/res/sounds/morse_ask.wav b/res/sounds/morse_ask.wav index 4f912eb44..19167852f 100644 Binary files a/res/sounds/morse_ask.wav and b/res/sounds/morse_ask.wav differ diff --git a/res/sounds/morse_do.wav b/res/sounds/morse_do.wav index f079f43a2..159365e3f 100644 Binary files a/res/sounds/morse_do.wav and b/res/sounds/morse_do.wav differ diff --git a/res/sounds/morse_do_ao.wav b/res/sounds/morse_do_ao.wav index 5dd242bbb..5ea764a72 100644 Binary files a/res/sounds/morse_do_ao.wav and b/res/sounds/morse_do_ao.wav differ diff --git a/res/sounds/morse_do_hud.wav b/res/sounds/morse_do_hud.wav index 4bf43de43..2ac48837a 100644 Binary files a/res/sounds/morse_do_hud.wav and b/res/sounds/morse_do_hud.wav differ diff --git a/src/Apps/oc_OwnerOnlineCheck.lsl b/src/Apps/oc_OwnerOnlineCheck.lsl index 79101b55b..a6ea8a066 100644 --- a/src/Apps/oc_OwnerOnlineCheck.lsl +++ b/src/Apps/oc_OwnerOnlineCheck.lsl @@ -58,7 +58,7 @@ integer bool(integer a){ if(a)return TRUE; else return FALSE; } -list g_lCheckboxes=["▢", "▣"]; +list g_lCheckboxes= ["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; } diff --git a/src/Apps/oc_badwords.lsl b/src/Apps/oc_badwords.lsl index 57324def4..dbdc3cd19 100644 --- a/src/Apps/oc_badwords.lsl +++ b/src/Apps/oc_badwords.lsl @@ -4,6 +4,8 @@ // littlemousy, Karo Weirsider, Nori Ovis, Ray Zopf et al. // Licensed under the GPLv2. See LICENSE for full details. +//Phuk +// May 2023 - reset script on transfer string g_sAppVersion = "¹⋅³"; @@ -442,6 +444,7 @@ default { changed(integer iChange) { if (iChange & CHANGED_INVENTORY) PermsCheck(); + if (iChange & CHANGED_OWNER) llResetScript(); /*if (iChange & CHANGED_REGION) { if (g_iProfiled) { llScriptProfiler(1); diff --git a/src/Apps/oc_capture.lsl b/src/Apps/oc_capture.lsl index fce02e944..29f36fa8d 100644 --- a/src/Apps/oc_capture.lsl +++ b/src/Apps/oc_capture.lsl @@ -6,10 +6,10 @@ Copyright 2021 Aria (Tashia Redrose) * Dec 2019 - Rewrote Capture & Reset Script Version to 1.0 + * Ping (Pingout Duffield), Nikki Lacrima * July 2023 - Owner ability to end Capture, Prevent BLOCKED from using Capture et al. - Licensed under the GPLv2. See LICENSE for full details. https://github.com/OpenCollarTeam/OpenCollar */ @@ -22,13 +22,13 @@ integer bool(integer a){ if(a)return TRUE; else return FALSE; } -list g_lCheckboxes=["⬜","⬛"]; +list g_lCheckboxes=["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; } -string g_sScriptVersion = "8.0"; +string g_sScriptVersion = "8.3"; string g_sAppVersion = "1.1"; DebugOutput(key kDest, list lParams){ @@ -70,6 +70,7 @@ integer in_range(key kID){ } } integer NOTIFY = 1002; +integer NOTIFY_OWNERS=1003; integer LINK_CMD_DEBUG=1999; integer REBOOT = -1000; @@ -140,9 +141,12 @@ UserCommand(integer iNum, string sStr, key kID) { return; } if (sStr==g_sSubMenu || sStr == "menu "+g_sSubMenu) { - if(iNum == CMD_OWNER) + if(g_iCaptured && (iNum == CMD_OWNER || (iNum == CMD_WEARER && !g_iRisky))){ // ask stop capture before any other changes to capture settings. + StopCapture(kID, iNum); + } + else if(iNum == CMD_OWNER) Menu(kID, iNum); - else if (kID == g_kCaptor) StopCapture(kID, iNum); // if we are the Captor ask if we want to stop capture instead. + else if (kID == g_kCaptor) StopCapture(kID, iNum); else llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to capture settings", kID); } //else if (iNum!=CMD_OWNER && iNum!=CMD_TRUSTED && kID!=g_kWearer) RelayNotify(kID,"Access denied!",0); @@ -180,11 +184,12 @@ UserCommand(integer iNum, string sStr, key kID) { } }else if(sChangevalue==""){ // Attempt to capture - if(!g_iEnabled)return; + if(!g_iEnabled && !g_iCaptured)return; //PR #858 if(g_iCaptured){ - // Check if ID is captor, as they may be trying to release - if(kID == g_kCaptor){ + // Check if ID is captor or owner, as they may be trying to release + if((kID == g_kCaptor)||(iNum == CMD_OWNER)){ // Initiate release now + llMessageLinked(LINK_SET, NOTIFY_OWNERS, llGetUsername(g_kWearer)+" is no longer captured by secondlife:///app/agent/"+(string)g_kCaptor+"/about", g_kWearer); llMessageLinked(LINK_SET, NOTIFY, "0Capture has ended", g_kCaptor); llMessageLinked(LINK_SET, NOTIFY, "0You are no longer captured by secondlife:///app/agent/"+(string)g_kCaptor+"/about !", g_kWearer); g_iCaptured=FALSE; @@ -210,7 +215,8 @@ UserCommand(integer iNum, string sStr, key kID) { g_kCaptor=kID; llMessageLinked(LINK_SET, NOTIFY, "0Successfuly captured secondlife:///app/agent/"+(string)g_kWearer+"/about", g_kCaptor); g_iCaptured=TRUE; - llMessageLinked(LINK_SET, NOTIFY, "0You have been captured by secondlife:///app/agent/"+(string)g_kCaptor+"/about ! If you need to free yourself, you can always use your safeword '"+g_sSafeword+"'. Also by saying your prefix capture", g_kWearer); + llMessageLinked(LINK_SET, NOTIFY_OWNERS, llGetUsername(g_kWearer)+" is captured by secondlife:///app/agent/"+(string)g_kCaptor+"/about", g_kWearer); + llMessageLinked(LINK_SET, NOTIFY, "0You have been captured by secondlife:///app/agent/"+(string)g_kCaptor+"/about ! If you need to free yourself, you can always use your safeword '"+g_sSafeword+"'.", g_kWearer); Commit(); } else { // Ask the wearer for consent to allow capture @@ -322,6 +328,10 @@ state active } link_message(integer iSender,integer iNum,string sStr,key kID){ + if(iNum == CMD_BLOCKED) { + llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to capture because you have been blocked on this collar", kID); + return; + } if(iNum == CMD_NOACCESS && sStr == "menu" && g_iEnabled && !g_iCaptured && in_range(kID)) StartCapture(kID, iNum); // When the collar is touched by someone without permission and capture is enabled show the Capture dialog. if(iNum >= CMD_OWNER && iNum <= CMD_NOACCESS) UserCommand(iNum, sStr, kID); if(iNum == MENUNAME_REQUEST && sStr == g_sParentMenu) @@ -395,6 +405,7 @@ state active } else if(sMsg == "YES"){ g_iCaptured=TRUE; llMessageLinked(LINK_SET, NOTIFY, "0Success", g_kCaptor); + llMessageLinked(LINK_SET, NOTIFY_OWNERS, llGetUsername(g_kWearer)+" is captured by secondlife:///app/agent/"+(string)g_kCaptor+"/about", g_kWearer); Commit(); } g_iExpire=0; @@ -408,8 +419,10 @@ state active llMessageLinked(LINK_SET, NOTIFY, "0Confirmed. Not Ending Capture", g_kExpireFor); }else if(sMsg == "YES"){ llMessageLinked(LINK_SET, NOTIFY, "1Capture has ended", g_kCaptor); + llMessageLinked(LINK_SET, NOTIFY_OWNERS, llGetUsername(g_kWearer)+" is no longer captured by secondlife:///app/agent/"+(string)g_kCaptor+"/about", g_kWearer); g_iCaptured=FALSE; g_kCaptor=NULL_KEY; + if (iNum == CMD_OWNER) Menu(kID, iNum); // Capture ended by owner, now show Menu Commit(); } @@ -437,8 +450,7 @@ state active if(Flag&2)g_iRisky=TRUE; if(Flag&4)g_iCaptured=TRUE; if(Flag&8)g_iAutoRelease=TRUE; - - + if(g_iCaptured && g_iAutoRelease) llSetTimerEvent(10); // #858 g_iFlagAtLoad=Flag; } } else if(llList2String(lSettings,0) == "auth"){ @@ -508,5 +520,4 @@ state active } } - } diff --git a/src/Apps/oc_detach.lsl b/src/Apps/oc_detach.lsl index 16f8080bb..7272c41d0 100644 --- a/src/Apps/oc_detach.lsl +++ b/src/Apps/oc_detach.lsl @@ -7,8 +7,9 @@ Copyright ©2021 Aria (Tashia Redrose) * Dec 2019 - Rewrote Outfits & Reset Script Version to 1.0 - - + +Medea (Medea Destiny) + Nov 2023 - Fix to ignore case (i.e either "menu detach" or "menu Detach") issue #875 et al. Licensed under the GPLv2. See LICENSE for full details. @@ -23,7 +24,7 @@ integer bool(integer a){ if(a)return TRUE; else return FALSE; } -list g_lCheckboxes=["⬜","⬛"]; +list g_lCheckboxes=["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; } @@ -84,22 +85,24 @@ Menu(key kID, integer iAuth) { } UserCommand(integer iNum, string sStr, key kID) { - if (llSubStringIndex(sStr,llToLower(g_sSubMenu)) && sStr != "menu "+g_sSubMenu) return; + sStr=llToLower(sStr); + //if (llSubStringIndex(sStr,llToLower(g_sSubMenu)) && sStr != "menu "+llToLower(g_sSubMenu)) return; + + if (sStr==llToLower(g_sSubMenu) || sStr == "menu "+llToLower(g_sSubMenu)) Menu(kID, iNum); - if (sStr==g_sSubMenu || sStr == "menu "+g_sSubMenu) Menu(kID, iNum); //else if (iNum!=CMD_OWNER && iNum!=CMD_TRUSTED && kID!=g_kWearer) RelayNotify(kID,"Access denied!",0); - else { + /* else { integer iWSuccess = 0; string sChangetype = llList2String(llParseString2List(sStr, [" "], []),0); string sChangevalue = llList2String(llParseString2List(sStr, [" "], []),1); string sText; - } + }*/ } key g_kWearer; list g_lMenuIDs; -integer g_iMenuStride; +integer g_iMenuStride = 3; integer g_iLocked=FALSE; integer ALIVE = -55; integer READY = -56; diff --git a/src/Apps/oc_outfits.lsl b/src/Apps/oc_outfits.lsl index c3b7bd845..3800b2a56 100644 --- a/src/Apps/oc_outfits.lsl +++ b/src/Apps/oc_outfits.lsl @@ -18,7 +18,12 @@ Lillith (Lillith Xue) Phidoux (Taya Maruti) *Sept 2022 - Limited core lock/unlock to wearer and owner (issue #855) et al. - + *Mar 2023 - Fixed lock issues with "~" and some navigation errors. (issue #910) + *June 2023 - Fixed Lock issue kAv instead of kID on the button check. + *June 20 2023 - Fixed Stray ~ that was causing detach to not work. +Medea (Medea Destiny) + *Nov 2023 - Fix for #902 (kAV instead of kID on button check) was not complete, + No Access notify also changed to kAV Licensed under the GPLv2. See LICENSE for full details. https://github.com/OpenCollarTeam/OpenCollar @@ -27,8 +32,8 @@ https://github.com/OpenCollarTeam/OpenCollar string g_sParentMenu = "Apps"; string g_sSubMenu = "Outfits"; -string g_sAppVersion = "1.6"; -//string g_sScriptVersion = "8.0"; +string g_sAppVersion = "1.7"; +//string g_sScriptVersion = "8.3"; //MESSAGE MAP @@ -81,7 +86,7 @@ integer bool(integer a){ if(a)return TRUE; else return FALSE; } -list g_lCheckboxes=["▢", "▣"]; +list g_lCheckboxes=["□","▣"]; string TickBox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; } @@ -239,6 +244,7 @@ UserCommand(integer iNum, string sStr, key kID) { g_sPath=GetFolderName(FALSE)+"/"+sChangevalue; g_iListenTimeout=0; } + llOwnerSay("[command "+sChangetype+"]"+g_sPath); if(!g_iLocked){ llOwnerSay("@detach=n"); @@ -247,6 +253,7 @@ UserCommand(integer iNum, string sStr, key kID) { ForceLockCore(); TickBrowser(); llSetTimerEvent(1); + llOwnerSay("[command "+sChangetype+"] Attempt detach"); if(!Bool((g_iAccessBitSet&32))){ if(llSubStringIndex(g_sPath, GetFolderName(TRUE))==-1) llOwnerSay("@detachall:"+GetFolderName(FALSE)+"=force"); @@ -257,15 +264,25 @@ UserCommand(integer iNum, string sStr, key kID) { } llSleep(2); // incase of lag string sAppend; - if(g_iRLVa)sAppend=RLVA_APPEND; + if(g_iRLVa) + { + sAppend=RLVA_APPEND; + } + else + { + sAppend=RLV_APPEND; + } if (g_sPath == GetOutfitSystem(FALSE)+"/" || g_sPath == GetOutfitSystem(FALSE)+"/"+sAppend) g_sLastOutfit = "NONE"; else g_sLastOutfit=g_sPath; + if(!g_iLocked){ + llOwnerSay("@detach=y"); + } RmCorelock(); llSleep(1); } if(sChangetype == "wear"){ //This looks like dead code TODO: Verify for removal? - if (g_sPath != "" && g_sPath != ".") llOwnerSay("@attachallover:"+g_sPath+"=force"); + if (g_sPath != "~" && g_sPath != ".") llOwnerSay("@attachallover:"+g_sPath+"=force"); } } } @@ -307,11 +324,14 @@ ForceLockCore(){ llOwnerSay("@detachallthis:"+GetOutfitSystem(TRUE)+"=y"); llSleep(1); llOwnerSay("@detachallthis:"+GetOutfitSystem(TRUE)+"=n"); - llSleep(0.5); + llSleep(1); // origionaly 0.5 may be to short and causes ~.core to be open during detach process. } RmCorelock(){ - llOwnerSay("@detachallthis:"+GetOutfitSystem(TRUE)+"=y"); + if(!g_iLockCore) + { + llOwnerSay("@detachallthis:"+GetOutfitSystem(TRUE)+"=y"); + } } integer ALIVE = -55; @@ -321,6 +341,7 @@ integer STARTUP = -57; integer g_iRLVa = FALSE; string RLVA_APPEND="."; +string RLV_APPEND="~"; string GetOutfitSystem(integer iCorePath){ if(g_iRLVa){ @@ -417,11 +438,10 @@ state active if(sMsg == UPMENU) { iRespring=FALSE; llMessageLinked(LINK_SET, iAuth, "menu "+g_sParentMenu, kAv); - } - else if(sMsg == TickBox(g_iLockCore, "Lock Core")){ - if(iAuth != CMD_OWNER && kID != g_kWearer) { - llMessageLinked(LINK_SET,NOTIFY, "0%NOACCESS% to core", kID); - return; + }else if(sMsg == TickBox(g_iLockCore, "Lock Core")){ + if(iAuth != CMD_OWNER && kAv != g_kWearer) { + llMessageLinked(LINK_SET,NOTIFY, "0%NOACCESS% to core", kAv); + return; } g_iLockCore=1-g_iLockCore; llSetTimerEvent(120); diff --git a/src/Apps/oc_spy.lsl b/src/Apps/oc_spy.lsl index 3a33d420c..1271e08cd 100644 --- a/src/Apps/oc_spy.lsl +++ b/src/Apps/oc_spy.lsl @@ -4,6 +4,8 @@ This file is a part of OpenCollar. Copyright 2020 : Contributors : +Nikki Lacrima (Nov 2023) + - Check for empty owner list, replaces faulty "runaway" check. Aria (tiff589/Tashia Redrose) - (Feb 2020) - Misc fixes - Updated checkboxes to use the checkbox function, which made checking in the dialog_response section easier as well. @@ -51,7 +53,7 @@ https://github.com/OpenCollarTeam/OpenCollar * 1.0 - Sharie Criss: Initial Alpha Release */ -string g_sScriptVersion = "8.0"; +string g_sScriptVersion = "8.3"; string g_sAppVersion = "1.1"; string g_sParentMenu = "Apps"; @@ -137,7 +139,7 @@ integer bool(integer a) { else return FALSE; } -list g_lCheckboxes=["⬜","⬛"]; +list g_lCheckboxes=["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; @@ -229,15 +231,6 @@ SaveSettings() { UserCommand(integer iNum, string sStr, key kID) { if (iNumCMD_WEARER) return; if (llSubStringIndex(sStr,llToLower(g_sSubMenu)) && sStr != "menu "+g_sSubMenu) return; - if ((iNum == CMD_OWNER || iNum == CMD_WEARER )&& sStr == "runaway") { - g_lOwner = []; - llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_subchat",""); - llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_attchat",""); - llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_radar",""); - llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_trace",""); - g_itrace = g_iradar = g_isubchat = g_iattchat = FALSE; - return; - } if (sStr==g_sSubMenu || sStr == "menu "+g_sSubMenu) { Menu(kID, iNum); } else if (iNum!=CMD_OWNER || kID==g_kWearer) { // If it's the wearer or not an owner.... @@ -534,6 +527,13 @@ default } else if(llList2String(lSettings,0)=="auth"){ if(llList2String(lSettings,1)=="owner") { g_lOwner = llParseString2List(llList2String(lSettings,2), [","], []); + if (g_lOwner == []) { + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_subchat",""); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_attchat",""); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_radar",""); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_trace",""); + g_itrace = g_iradar = g_isubchat = g_iattchat = FALSE; + } } } } else if(iNum == LM_SETTING_DELETE) { @@ -546,6 +546,14 @@ default else if(llList2String(lSettings,1)=="attchat") g_iattchat=FALSE; else if(llList2String(lSettings,1)=="radar") g_iradar=FALSE; else if(llList2String(lSettings,1)=="trace") g_itrace=FALSE; + } else if (sStr == "auth_owner") { + g_lOwner = []; + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_subchat",""); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_attchat",""); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_radar",""); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "spy_trace",""); + g_itrace = g_iradar = g_isubchat = g_iattchat = FALSE; + return; } } else if(iNum == LINK_CMD_DEBUG) { integer onlyver =0; diff --git a/src/Apps/oc_timer.lsl b/src/Apps/oc_timer.lsl index 958b51ad3..10a58302c 100644 --- a/src/Apps/oc_timer.lsl +++ b/src/Apps/oc_timer.lsl @@ -1,580 +1,578 @@ -// This file is part of OpenCollar. -// Copyright (c) 2008 - 2017 Satomi Ahn, Nandana Singh, Joy Stipe, -// Wendy Starfall, Master Starship, Medea Destiny, littlemousy, -// Romka Swallowtail, Sumi Perl, Keiyra Aeon, Garvin Twine et al. -// Licensed under the GPLv2. See LICENSE for full details. - -string g_sAppVersion = "¹⋅⁴"; +/* +This file is a part of OpenCollar. +Created by Medea Destiny +Copyright ©2024 + +: Contributors : + +Medea (Medea Destiny) + *May 2024 - Created script +Ping (Pingout Duffield) + *Jul 2024 - Fixed typos in variable %WEARER% -> %WEARERNAME% +et al. +Licensed under the GPLv2. See LICENSE for full details. +https://github.com/OpenCollarTeam/OpenCollar +*/ -string g_sSubMenu = "Timer"; -string g_sParentMenu = "Apps"; -//MESSAGE MAP -//integer CMD_ZERO = 0; +integer CMD_ZERO = 0; integer CMD_OWNER = 500; -//integer CMD_TRUSTED = 501; -//integer CMD_GROUP = 502; +integer CMD_TRUSTED = 501; +integer CMD_GROUP = 502; integer CMD_WEARER = 503; integer CMD_EVERYONE = 504; -//integer CMD_RLV_RELAY = 507; -//integer CMD_SAFEWORD = 510; -//integer CMD_RELAY_SAFEWORD = 511; -//integer CMD_BLOCKED = 520; - -//integer ATTACHMENT_COMMAND = 602; -integer ATTACHMENT_FORWARD = 610; - -//integer WEARERLOCKOUT = 620; - -integer NOTIFY = 1002; -//integer NOTIFY_OWNERS = 1003; -integer REBOOT = -1000; +integer CMD_SAFEWORD = 510; -// integer LM_SETTING_SAVE = 2000; -integer LM_SETTING_RESPONSE = 2002; -// integer LM_SETTING_DELETE = 2003; -integer LM_SETTING_EMPTY = 2004; - -integer MENUNAME_REQUEST = 3000; -integer MENUNAME_RESPONSE = 3001; -integer MENUNAME_REMOVE = 3003; +//backwards compatibility stuff for float text; +integer LM_SETTING_RESPONSE = 2002;//the settings script sends responses on this channel +integer LM_SETTING_DELETE = 2003;//delete token from settings +integer REBOOT = -1000; +string UPMENU = "BACK"; integer DIALOG = -9000; integer DIALOG_RESPONSE = -9001; integer DIALOG_TIMEOUT = -9002; -// Added by WhiteFire -integer TIMER_EVENT = -10000; // str = "start" or "end". For start, either "online" or "realtime". - -string UPMENU = "BACK"; -list g_lTimeButtons = ["RESET","+00:01","+00:05","+00:30","+03:00","+24:00","-00:01","-00:05","-00:30","-03:00","-24:00"]; - -integer MAX_TIME=0x7FFFFFFF; - -list g_lTimes; -integer g_iTimesLength; -integer g_iCurrentTime; -integer g_iOnTime; -integer g_iLastTime; -integer g_iFirstOnTime; -integer g_iFirstRealTime; -integer g_iLastRez; -integer n;//for loops -string g_sMessage; -//these can change -integer REAL_TIME=1; -integer REAL_TIME_EXACT=5; -integer ON_TIME=3; -integer ON_TIME_EXACT=7; - -integer g_iInterfaceChannel; -// end time keeper - -integer g_iOnRunning; -integer g_iOnSetTime; -integer g_iOnTimeUpAt; -integer g_iRealRunning; -integer g_iRealSetTime; -integer g_iRealTimeUpAt; +integer MENUNAME_REQUEST = 3000; +integer MENUNAME_RESPONSE = 3001; +integer MENUNAME_REMOVE = 3003; -integer g_iCollarLocked; -integer g_iUnlockCollar = 0; -integer g_iClearRLVRestions = 0; -integer g_iUnleash = 0; -integer g_iBoth = 0; -integer g_iWhoCanChangeTime = 504; -integer g_iWhoCanChangeLeash = 504; +integer NOTIFY = 1002; -integer g_iTimeChange; +string LSDPrefix="timer"; //All LSD used by this script has this prefix. +integer LSD_REQUEST=-500; //Sends LSDPrefix when received, for flushing no longer used LSD. +integer LSD_RESPONSE=-501; //Reply to above -list g_lLocalMenu; +integer AUTH_WEARERLOCKOUT=602; -key g_kWearer; -// handles -list g_lMenuIDs; +string g_sSubMenu = "Timer"; +string g_sParentMenu = "Apps"; +list g_lMenuIDs; //three strided list of avkey, dialogid, and menuname integer g_iMenuStride = 3; +key g_kMenuUser; +key g_kWearer; -/* -integer g_iProfiled; -Debug(string sStr) { - //if you delete the first // from the preceeding and following lines, - // profiling is off, debug is off, and the compiler will remind you to - // remove the debug calls from the code, we're back to production mode - if (!g_iProfiled){ - g_iProfiled=1; - llScriptProfiler(1); - } - llOwnerSay(llGetScriptName() + "(min free:"+(string)(llGetMemoryLimit()-llGetSPMaxMemory())+")["+(string)llGetFreeMemory()+"] :\n" + sStr); -} -*/ - -Dialog(key kID, string sPrompt, list lChoices, list lUtility, integer iPage, integer iAuth, string sName) { +Dialog(key kID, string sPrompt, list lChoices, list lUtilityButtons, integer iPage, integer iAuth, string sName) { key kMenuID = llGenerateKey(); - llMessageLinked(LINK_THIS, DIALOG, (string)kID + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lChoices, "`") + "|" + llDumpList2String(lUtility, "`") + "|" + (string)iAuth, kMenuID); + llMessageLinked(LINK_SET, DIALOG, (string)kID + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lChoices, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, kMenuID); - integer i = llListFindList(g_lMenuIDs, [kID]); - if (~i) g_lMenuIDs = llListReplaceList(g_lMenuIDs, [kID, kMenuID, sName], i, i + g_iMenuStride - 1); + integer iIndex = llListFindList(g_lMenuIDs, [kID]); + if (~iIndex) g_lMenuIDs = llListReplaceList(g_lMenuIDs, [kID, kMenuID, sName], iIndex, iIndex + g_iMenuStride - 1); else g_lMenuIDs += [kID, kMenuID, sName]; } - -DoMenu(key keyID, integer iAuth) { - //Debug("timeremaning:"+(string)(g_iOnTimeUpAt-g_iOnTime)); - string sPrompt = "\n[Legacy Timer]\t"+g_sAppVersion+"\n\nA frozen pizza takes ~12 min to bake.\n"; - list lMyButtons = ["Real Timer","Online Timer"]; - - sPrompt += "\n Online Timer: "+Int2Time(g_iOnSetTime); - if (g_iOnRunning==1) sPrompt += "\n Online Timer: "+Int2Time(g_iOnTimeUpAt-g_iOnTime)+" left\n"; - else sPrompt += "\n Online Timer: not running\n"; - - sPrompt += "\n RL Timer: "+Int2Time(g_iRealSetTime); - if (g_iRealRunning==1) sPrompt += "\n RL Timer: "+Int2Time(g_iRealTimeUpAt-g_iCurrentTime)+" left"; - else sPrompt += "\n RL Timer: not running"; - - if (g_iBoth) lMyButtons += ["☒ combined"]; - else lMyButtons += ["☐ combined"]; - - if (g_iUnlockCollar) lMyButtons += ["☑ unlock"]; - else lMyButtons += ["☐ unlock"]; - - if (g_iUnleash) lMyButtons += ["☑ unleash"]; - else lMyButtons += ["☐ unleash"]; - - if (g_iClearRLVRestions) lMyButtons += ["☑ clear RLV"]; - else lMyButtons += ["☐ clear RLV"]; - - if (g_iRealRunning || g_iOnRunning) lMyButtons += ["STOP","RESET"]; - else if (g_iRealSetTime || g_iOnSetTime) lMyButtons += ["START","RESET"]; - - Dialog(keyID, sPrompt, lMyButtons + g_lLocalMenu, [UPMENU], 0, iAuth, "menu"); +string buttonize(string token) +{ + string t=LSDRead(token); + if(t=="0" || t=="") return "□ "+token; + else return "▣ "+token; + } -DoOnMenu(key keyID, integer iAuth) { - string sPrompt = "\n Online Time Settings\n"; - sPrompt += "\n Online Timer: "+Int2Time(g_iOnSetTime); - if (g_iOnRunning) sPrompt += "\n Online Timer: "+Int2Time(g_iOnTimeUpAt-g_iOnTime)+" left"; - else sPrompt += "\n Online Timer: not running"; - Dialog(keyID, sPrompt, g_lTimeButtons, [UPMENU], 0, iAuth, "online"); +mainMenu(key kAv, integer iAuth) +{ + list buttons; + string sPrompt="Timer menu \n\n"; + + + if(LSDRead("TimerActive")=="1") + { + integer tl=(integer)LSDRead("LockTime")-(integer)llGetTime(); + if(tl<10) tl=10; + sPrompt+="Active! Time left:"+timeDisplay(tl)+"\n\nClick 'EndNow' to end timer early, or 'Cancel' to stop the timer without triggering commands.\nPermissive mode allows collar users (apart from wearer) to end the timer even if they have lower auth than the timer setter. Titler shows time remaining as float text unless a title is set."; + buttons+=["EndNow","Cancel",buttonize("Permissive")]; + } + else + { + buttons=["Time",buttonize("Lockout"),buttonize("Customs"),buttonize("Unleash"),buttonize("Unpose"),buttonize("Unsit"),buttonize("ClearRLV"),buttonize("Titler"),buttonize("Permissive"),"Start"]; + sPrompt+="Timer: "+timeDisplay((integer)LSDRead("LockTime"))+"\n\n'Start' to activate timer. 'Time' to change timer length. 'Lockout' locks wearer out of collar when timer active.\n On timer end: 'ClearRLV' clears restrictions, 'Unsit' makes wearer stand (even strict sit if owner sets timer), 'Unpose' and 'Unleash' as they say, 'Custom' sets custom commands (checked if any set).\n Permissive lets collar users (not wearer) to end timer even if lower auth than the setter. Titler shows time remaining as float text unless a title is set."; + } + Dialog(kAv,sPrompt,buttons,[UPMENU],0,iAuth,"timer~app"); } - -DoRealMenu(key keyID, integer iAuth) { - string sPrompt = "\n RL Time Settings\n"; - sPrompt += "\n RL timer: " + Int2Time(g_iRealSetTime); - if (g_iRealRunning) sPrompt += "\n RL Timer: "+Int2Time(g_iRealTimeUpAt-g_iCurrentTime)+" left"; - else sPrompt += "\n RL Timer: not running"; - Dialog(keyID, sPrompt, g_lTimeButtons, [UPMENU], 0, iAuth, "real"); +customMenu(key kAv,integer iAuth) +{ + string text=LSDRead("Customs"); + if(text=="") text="None."; + else text=llDumpList2String(llCSV2List(text),"\n"); + text="Enter chat commands (without prefix, e.g. 'preset clear dress' or 'tp home') separating commands with a comma. Leave blank to clear.\nCurrent: "+text; + Dialog(kAv,text,[],[],0,iAuth,"timer~custom"); +} +timerMenu(key kAv, integer iAuth) +{ + Dialog(kAv,"Set timer time\n\nCurrent time: "+timeDisplay((integer)LSDRead("LockTime"))+"\n",["+1 min","+5 min","+30 min","-1 min","-5 min","-30 min","+1 hr","+6 hrs","-6 hrs","-1 hr"],[UPMENU],0,iAuth,"timer~timerset"); } -string Int2Time(integer sTime) { - if (sTime<0) sTime=0; - integer iSecs=sTime%60; - sTime = (sTime-iSecs)/60; - integer iMins=sTime%60; - sTime = (sTime-iMins)/60; - integer iHours=sTime%24; - integer iDays = (sTime-iHours)/24; - - //this is the onley line that needs changing... - return ( (string)iDays+" days "+ - llGetSubString("0"+(string)iHours,-2,-1) + ":"+ - llGetSubString("0"+(string)iMins,-2,-1) + ":"+ - llGetSubString("0"+(string)iSecs,-2,-1) ); - //return (string)iDays+":"+(string)iHours+":"+(string)iMins+":"+(string)iSecs; +string LSDRead(string token) +{ + return llLinksetDataRead(LSDPrefix+"_"+token); } - -TimerFinish() { - if (g_iBoth && (g_iOnRunning == 1 || g_iRealRunning == 1)) return; -// llMessageLinked(LINK_SET, WEARERLOCKOUT, "off", ""); - if (g_iUnlockCollar) llMessageLinked(LINK_SET, CMD_OWNER, "unlock", g_kWearer); - if (g_iClearRLVRestions) { - llMessageLinked(LINK_SET, CMD_OWNER, "clear", g_kWearer); - if (!g_iUnlockCollar && g_iCollarLocked) { - llSleep(2); - llMessageLinked(LINK_SET, CMD_OWNER, "lock", g_kWearer); - } - } - if (g_iUnleash && g_iWhoCanChangeTime <= g_iWhoCanChangeLeash) - llMessageLinked(LINK_SET, CMD_OWNER, "unleash", g_kWearer); - g_iUnlockCollar=g_iClearRLVRestions=g_iUnleash=0; - g_iOnSetTime=g_iRealSetTime=0; - g_iOnRunning=g_iRealRunning=0; - g_iOnTimeUpAt=g_iRealTimeUpAt=0; - g_iWhoCanChangeTime=504; - llMessageLinked(LINK_THIS,CMD_OWNER,"lockout false",""); - llMessageLinked(LINK_THIS,NOTIFY,"0"+"Yay! Timer expired!",g_kWearer); - llMessageLinked(LINK_SET, TIMER_EVENT, "end", ""); +LSDWrite(string token, string val) +{ + llLinksetDataWrite(LSDPrefix+"_"+token,val); } +/* LSD settings +timer_Lockout +timer_Unleash +timer_UnPose +timer_Unsit +timer_ClearRLV +timer_Customs +timer_TimerSetterKey +timer_LastAuth +timer_LockTime +timer_TimerActive +timer_Permissive +timer_Titler +*/ +reset() +{ + list defaults=["Lockout","0","Unleash","0","Unpose","0","ClearRLV","0","Unsit","0","Customs","","TimerSetterKey","","LastAuth","0","LockTime","600","TimerActive","0","Permissive","0","Titler","0"]; + integer i=llGetListLength(defaults); + integer x; + while(xCMD_EVERYONE) return; + sCmd=llToLower(sCmd); + if (kAv==g_kWearer && sCmd == "runaway") + { + stopTimer(FALSE); + return; } - - integer FULL_PERMS = PERM_COPY | PERM_MODIFY | PERM_TRANSFER; - if (!((llGetInventoryPermMask(sName,MASK_OWNER) & FULL_PERMS) == FULL_PERMS)) { - llOwnerSay("The " + sName + " script is not mod/copy/trans. This is a violation of the OpenCollar license. Please ask the person who gave you this script for a full-perms replacement."); + if(sCmd==llToLower(g_sSubMenu) || sCmd=="menu timer") + { + mainMenu(kAv,iAuth); + return; } - - if (!((llGetInventoryPermMask(sName,MASK_NEXT) & FULL_PERMS) == FULL_PERMS)) { - llOwnerSay("You have removed mod/copy/trans permissions for the next owner of the " + sName + " script. This is a violation of the OpenCollar license. Please make the script full perms again."); + list lParams=llParseString2List(sCmd,[" "],[]); + if(llList2String(lParams,0)!="timer") return; + integer remenu; + if(llList2String(lParams,-1)=="remenu") + { + remenu=TRUE; + lParams=llDeleteSubList(lParams,-1,-1); } -} - - -UserCommand(integer iAuth, string sStr, key kID, integer iMenu) { - if ((g_iOnRunning || g_iRealRunning) && kID == g_kWearer) { - if (!llSubStringIndex(llToLower(sStr),"timer")) { - llMessageLinked(LINK_THIS,NOTIFY,"0"+"You can't access here until the timer went off.",kID); + string sCmd=llList2String(lParams,1); + string sVal=llList2String(lParams,2); + if(sVal=="on") sVal="1"; + else if(sVal=="off") sVal="0"; + if(sCmd=="permissive" && (sVal=="1" || sVal=="0")) + { + if(iAuth!=CMD_OWNER && kAv!=g_kWearer) llRegionSayTo(kAv,0,"Only wearer and owners can change this setting."); + else if(LSDRead("TimerActive")=="1" && kAv==g_kWearer) llOwnerSay("You cannot change this while the timer is active."); + else + { + LSDWrite("Permissive",sVal); + llMessageLinked(LINK_THIS,NOTIFY,"1Setting timer permissive to "+llList2String(["off","on"],(integer)sVal)+".",kAv); + } + } + else if(LSDRead("TimerActive")=="1") + { + if(iAuth>(integer)LSDRead("LastAuth")&& (LSDRead("Permissive")!="1" || kAv==g_kWearer)) + { + llMessageLinked(LINK_THIS,NOTIFY,"0Timer has been set by a user with higher auth than you, you can't override the current timer or settings.",kAv); + if(remenu==TRUE && (kAv!=g_kWearer || LSDRead("Lockout")=="0") ) mainMenu(kAv,iAuth); return; } + if(sCmd=="endnow") + { + llMessageLinked(LINK_THIS,NOTIFY,"1%WEARERNAME%'s timer ended by secondlife:///app/agent/"+(string)kAv+"/about",kAv); + stopTimer(TRUE); + } + else if(sCmd=="cancel") + { + llMessageLinked(LINK_THIS,NOTIFY,"1%WEARERNAME%'s timer cancelled by secondlife:///app/agent/"+(string)kAv+"/about",kAv); + stopTimer(FALSE); + } + else llRegionSayTo(kAv,0,"Sorry, can't change that while timer is active."); } - if (llToLower(sStr) == "rm timer" && (iAuth == CMD_OWNER || kID == g_kWearer)) - Dialog(kID, "\nDo you really want to uninstall the "+g_sSubMenu+" App?", ["Yes","No","Cancel"], [], 0, iAuth,"rmtimer"); - if (llToLower(sStr) == "timer" || sStr == "menu "+g_sSubMenu) DoMenu(kID, iAuth); - else if (llGetSubString(sStr, 0, 5) == "timer ") { - //Debug(sStr); - string sMsg = llGetSubString(sStr, 6, -1); - //we got a response for something we handle locally - if (sMsg == "START") { - TimerStart(iAuth); - if (kID == g_kWearer) iMenu = FALSE; - } else if (sMsg == "STOP") TimerFinish(); - else if (sMsg == "RESET") { - g_iRealSetTime = g_iRealTimeUpAt = 0; - g_iOnSetTime = g_iOnTimeUpAt = 0; - if (g_iRealRunning == 1 || g_iOnRunning == 1) { - g_iRealRunning = 0; - g_iOnRunning = 0; - TimerFinish(); - } - } else if (sMsg == "☒ combined") g_iBoth = FALSE; - else if (sMsg == "☐ combined") g_iBoth = TRUE; - else if (sMsg == "☑ unlock") { - if (iAuth == CMD_OWNER) g_iUnlockCollar = 0; - else llMessageLinked(LINK_THIS,NOTIFY,"0"+"%NOACCESS%",kID); - } else if (sMsg == "☐ unlock") { - if (iAuth == CMD_OWNER) g_iUnlockCollar = 1; - else llMessageLinked(LINK_THIS,NOTIFY,"0"+"%NOACCESS%",kID); - } else if (sMsg == "☑ clear RLV") { - if (iAuth == CMD_WEARER) llMessageLinked(LINK_THIS,NOTIFY,"0"+"%NOACCESS%",kID); - else g_iClearRLVRestions = 0; - } else if (sMsg == "☐ clear RLV") { - if (iAuth == CMD_WEARER) llMessageLinked(LINK_THIS,NOTIFY,"0"+"%NOACCESS%",kID); - else g_iClearRLVRestions = 1; - } else if (sMsg == "☑ unleash") { - if (iAuth <= g_iWhoCanChangeLeash) g_iUnleash = 0; - else llMessageLinked(LINK_THIS,NOTIFY,"0"+"%NOACCESS%",kID); - } else if (sMsg == "☐ unleash") { - if (iAuth <= g_iWhoCanChangeLeash) g_iUnleash = 1; - else llMessageLinked(LINK_THIS,NOTIFY,"0"+"%NOACCESS%",kID); - } else if (llGetSubString(sMsg, 0, 11) == "Online Timer") { - sMsg = "online" + llStringTrim(llGetSubString(sMsg, 12, -1), STRING_TRIM_HEAD); - } else if (llGetSubString(sMsg, 0, 9) == "Real Timer") { - sMsg = "real" + llStringTrim(llGetSubString(sMsg, 10, -1), STRING_TRIM_HEAD); + else if(sCmd=="start") startTimer(kAv,iAuth); + else if(sCmd=="time") + { + if(sVal=="") + { + timerMenu(kAv,iAuth); + return; } - - if (llGetSubString(sMsg, 0, 5) == "online") { - sMsg = llStringTrim(llGetSubString(sMsg, 6, -1), STRING_TRIM_HEAD); - if (iAuth <= g_iWhoCanChangeTime) { - list lTimes = llParseString2List(llGetSubString(sMsg, 1, -1), [":"], []); - if (sMsg == "RESET") { - g_iOnSetTime=g_iOnTimeUpAt=0; - if (g_iOnRunning == 1) { //unlock - g_iOnRunning=0; - TimerFinish(); - } - } else if (llGetSubString(sMsg, 0, 0) == "+") { - g_iTimeChange=llList2Integer(lTimes,0)*60*60+llList2Integer(lTimes,1)*60; - g_iOnSetTime += g_iTimeChange; - if (g_iOnRunning==1) g_iOnTimeUpAt += g_iTimeChange; - else if (g_iOnRunning==3) { - g_iOnTimeUpAt=g_iOnTime+g_iOnSetTime; - g_iOnRunning=1; - } - } else if (llGetSubString(sMsg, 0, 0) == "-") { - g_iTimeChange=-(llList2Integer(lTimes,0)*60*60+llList2Integer(lTimes,1)*60); - g_iOnSetTime += g_iTimeChange; - if (g_iOnSetTime<0) g_iOnSetTime=0; - if (g_iOnRunning==1) { - g_iOnTimeUpAt += g_iTimeChange; - if (g_iOnTimeUpAt<=g_iOnTime) { - //unlock - g_iOnRunning=g_iOnSetTime=g_iOnTimeUpAt=0; - TimerFinish(); - } - } - } else if (llGetSubString(sMsg, 0, 0) == "=") { - g_iTimeChange=llList2Integer(lTimes,0)*60*60+llList2Integer(lTimes,1)*60; - if (g_iTimeChange <= 0) return; // use clear. - g_iOnSetTime = g_iTimeChange; - if (g_iOnRunning==1) g_iOnTimeUpAt = g_iOnTime + g_iTimeChange; - else if (g_iOnRunning==3) { - g_iOnTimeUpAt=g_iOnTime + g_iTimeChange; - g_iOnRunning=1; - } - } else return; + else + { + //function to attempt to derive correct seconds value + //from whatever meaningful text input is given. + sVal=(string)llList2List(lParams,2,-1); + string sVal=(string)llList2List(lParams,2,-1); + integer ti; + list t=llParseString2List(sVal,[","],["hours","hrs","hr","hs","h"]); + if(llGetListLength(t)>1) + { + ti+=(integer)llList2String(t,0)*3600; + t=llDeleteSubList(t,0,1); + sVal=(string)t; } - if (iMenu) DoOnMenu(kID, iAuth); - return; - } else if (llGetSubString(sMsg, 0, 3) == "real") { - sMsg = llStringTrim(llGetSubString(sMsg, 4, -1), STRING_TRIM_HEAD); - list lTimes = llParseString2List(llGetSubString(sMsg, 1, -1), [":"], []); - if (iAuth <= g_iWhoCanChangeTime) { - if (sMsg == "RESET") { - g_iRealSetTime=g_iRealTimeUpAt=0; - if (g_iRealRunning == 1) { //unlock - g_iRealRunning=0; - TimerFinish(); - } - } else if (llGetSubString(sMsg, 0, 0) == "+") { - g_iTimeChange=llList2Integer(lTimes,0)*60*60+llList2Integer(lTimes,1)*60; - g_iRealSetTime += g_iTimeChange; - if (g_iRealRunning==1) g_iRealTimeUpAt += g_iTimeChange; - else if (g_iRealRunning==3) { - g_iRealTimeUpAt=g_iCurrentTime+g_iRealSetTime; - g_iRealRunning=1; - } - } else if (llGetSubString(sMsg, 0, 0) == "-") { - g_iTimeChange=-(llList2Integer(lTimes,0)*60*60+llList2Integer(lTimes,1)*60); - g_iRealSetTime += g_iTimeChange; - if (g_iRealSetTime<0) g_iRealSetTime=0; - if (g_iRealRunning==1) { - g_iRealTimeUpAt += g_iTimeChange; - if (g_iRealTimeUpAt<=g_iCurrentTime) { //unlock - g_iRealRunning=g_iRealSetTime=g_iRealTimeUpAt=0; - TimerFinish(); - } - } - } else if (llGetSubString(sMsg, 0, 0) == "=") { - g_iTimeChange=llList2Integer(lTimes,0)*60*60+llList2Integer(lTimes,1)*60; - if (g_iTimeChange <= 0) return ; // Not handled. - g_iRealSetTime = g_iTimeChange; - if (g_iRealRunning==1) g_iRealTimeUpAt = g_iCurrentTime+g_iRealSetTime; - else if (g_iRealRunning==3) { - g_iRealTimeUpAt=g_iCurrentTime+g_iRealSetTime; - g_iRealRunning=1; - } - } else return ; + t=llParseString2List(sVal,[],["minutes","mins","min","ms","m"]); + if(llGetListLength(t)>1) + { + ti+=(integer)llList2String(t,0)*60; + t=llDeleteSubList(t,0,1); + sVal=(string)t; + } + t=llParseString2List(sVal,[],["s"]); + ti+=(integer)llList2String(t,0); + if(ti) + { + LSDWrite("LockTime",(string)ti); + llMessageLinked(LINK_THIS,NOTIFY,"1Setting time to "+timeDisplay(ti)+".",kAv); + } + else llMessageLinked(LINK_THIS,NOTIFY,"0Invalid setting for timer time.",kAv); + } + } + else if(sCmd=="customs") + { + if(sVal==""||sVal=="1"||sVal=="0") customMenu(kAv,iAuth); + else + { + LSDWrite("Customs",sVal); + llMessageLinked(LINK_THIS,NOTIFY,"0Timer custom commands changed to:\n"+llDumpList2String(llCSV2List(sVal),"\n"),kAv); + } + return; + } + else if(sVal=="1" || sVal=="0") + { + + if(sCmd=="lockout") + { + if(kAv==g_kWearer) + { + LSDWrite("Lockout",sVal); + if(sVal=="1") llOwnerSay("You will be locked out of your collar when the timer is active."); + else llOwnerSay("Wearer lockout deactivated for timer"); } - if (iMenu) DoRealMenu(kID, iAuth); + else llMessageLinked(LINK_THIS,NOTIFY,"0Only wearer can modify timer self-lockout mode.",kAv); + } + else if(sCmd=="clearrlv") + { + LSDWrite("ClearRLV",sVal); + llMessageLinked(LINK_THIS,NOTIFY,"1Setting timer ClearRLV to "+llList2String(["off","on"],(integer)sVal)+".",kAv); + } + else if(sCmd=="unpose") + { + LSDWrite("Unpose",sVal); + llMessageLinked(LINK_THIS,NOTIFY,"1Setting timer Unpose to "+llList2String(["off","on"],(integer)sVal)+".",kAv); + } + else if(sCmd=="unleash") + { + LSDWrite("Unleash",sVal); + llMessageLinked(LINK_THIS,NOTIFY,"1Setting timer Unleash to "+llList2String(["off","on"],(integer)sVal)+".",kAv); + } + else if(sCmd=="unsit") + { + LSDWrite("Unsit",sVal); + llMessageLinked(LINK_THIS,NOTIFY,"1Setting timer Unsit to "+llList2String(["off","on"],(integer)sVal)+".",kAv); + } + else if(sCmd=="titler") + { + LSDWrite("Titler",sVal); + llMessageLinked(LINK_THIS,NOTIFY,"1Setting timer titling "+llList2String(["off","on"],(integer)sVal)+".",kAv); + } + else llRegionSayTo(kAv,0,"I didn't understand that command, sorry."); + + } + if(remenu==TRUE &&(kAv!=g_kWearer || llLinksetDataRead("auth_WearerLockout")=="")) mainMenu(kAv,iAuth); + + + +} + +startTimer(key kAv, integer iAuth) +{ + integer time=(integer)LSDRead("LockTime"); + llSetTimerEvent(60); + llResetTime(); + LSDWrite("TimerActive","1"); + LSDWrite("TimerSetterKey",(string)kAv); + LSDWrite("LastAuth",(string)iAuth); + if(LSDRead("Lockout")=="1") + { + list temp=llCSV2List(llLinksetDataRead("auth_WearerLockout")); + if(llListFindList(temp,[LSDPrefix])==-1) temp+=[LSDPrefix]; + llLinksetDataWrite("auth_WearerLockout",llList2CSV(temp)); + } + if(LSDRead("Titler")=="1" && g_iTitlerActive==FALSE) setText(TRUE); + string msg=timeDisplay(time)+" timer Started!\nWhen the timer ends, the following commands will be issued:\n"; + string cmds; + if(LSDRead("Unleash")=="1") cmds+="Unleash\n"; + if(LSDRead("Unpose")=="1") cmds+="Stop animations\n"; + if(LSDRead("ClearRLV")=="1") cmds+="Clear all restrictions\n"; + if(LSDRead("Unsit")=="1") cmds+="Unsit if seated (even with strict sit when timer set by owner)\n"; + list t=llCSV2List(LSDRead("Customs")); + cmds+=llDumpList2String(t,"\n"); + if(cmds=="") cmds="None"; + llMessageLinked(LINK_THIS,NOTIFY,"1"+msg+cmds,kAv); + +} +stopTimer(integer execute) +{ + llSetTimerEvent(0); + list temp=llCSV2List(llLinksetDataRead("auth_WearerLockout")); + integer i=llListFindList(temp,[LSDPrefix]); + if(i!=-1) temp=llDeleteSubList(temp,i,i); + llLinksetDataWrite("auth_WearerLockout",llList2CSV(temp)); + llMessageLinked(LINK_THIS,NOTIFY,"1%WEARERNAME%'s timelock has ended.",(key)LSDRead("TimerSetterKey")); + LSDWrite("TimerActive","0"); + if(execute) + { + if(LSDRead("Unleash")=="1") llMessageLinked(LINK_THIS,CMD_ZERO,"unleash",(key)LSDRead("TimerSetterKey")); + if(LSDRead("Unpose")=="1") llMessageLinked(LINK_THIS,CMD_ZERO,"stop",(key)LSDRead("TimerSetterKey")); + if(LSDRead("ClearRLV")=="1") llMessageLinked(LINK_THIS,CMD_ZERO,"clear",(key)LSDRead("TimerSetterKey")); + if(LSDRead("Unsit")=="1") llMessageLinked(LINK_THIS,CMD_ZERO,"unsit",(key)LSDRead("TimerSetterKey")); + list cTemp=llCSV2List(LSDRead("Customs")); + integer max=llGetListLength(cTemp); + i=0; + string cmd; + while(i; +integer g_iTextPrim; +integer g_iTitlerOffset=8; +ScanFloatText() +{ + integer end = llGetNumberOfPrims(); + integer i; + while(i 0) g_iInterfaceChannel = -g_iInterfaceChannel; - g_iFirstOnTime=MAX_TIME; - g_iFirstRealTime=MAX_TIME; - llRegionSayTo(g_kWearer, g_iInterfaceChannel, "timer|sendtimers"); - //Debug("Starting"); + reset(); + ScanFloatText(); + llMessageLinked(LINK_SET, ALIVE, llGetScriptName(),""); } + - link_message(integer iSender, integer iNum, string sStr, key kID) { - if (iNum >= CMD_OWNER && iNum <= CMD_EVERYONE) UserCommand(iNum, sStr, kID, FALSE); - else if (iNum==ATTACHMENT_FORWARD) { - list info = llParseString2List (sStr, ["|"], []); - if (llList2String(info, 0)!="timer") return; - //Debug(sStr); - string sCommand = llList2String(info, 1); - integer type = llList2Integer(info, 2); - if (sCommand=="settimer") { - //should check values but I am not yet. - if (type==REAL_TIME) { - integer newtime = llList2Integer(info, 3) +g_iCurrentTime; - g_lTimes=g_lTimes+[REAL_TIME,newtime]; - if (g_iFirstRealTime>newtime) g_iFirstRealTime=newtime; - g_sMessage="timer|timeis|"+(string)REAL_TIME+"|"+(string)g_iCurrentTime; - } else if (type==REAL_TIME_EXACT) { - integer newtime = llList2Integer(info, 3); - g_lTimes=g_lTimes+[REAL_TIME,newtime]; - if (g_iFirstRealTime>newtime) g_iFirstRealTime=newtime; - } else if (type==ON_TIME) { - integer newtime = llList2Integer(info, 3) +g_iOnTime; - g_lTimes=g_lTimes+[ON_TIME,newtime]; - if (g_iFirstOnTime>newtime) g_iFirstOnTime=newtime; - g_sMessage="timer|timeis|"+(string)ON_TIME+"|"+(string)g_iOnTime; - } else if (type==ON_TIME_EXACT) { - integer newtime = llList2Integer(info, 3) +g_iOnTime; - g_lTimes=g_lTimes+[ON_TIME,newtime]; - if (g_iFirstOnTime>newtime) g_iFirstOnTime=newtime; - } - } else if (sCommand=="gettime") { - if (type==REAL_TIME) g_sMessage="timer|timeis|"+(string)REAL_TIME+"|"+(string)g_iCurrentTime; - else if (type==ON_TIME) g_sMessage="timer|timeis|"+(string)ON_TIME+"|"+(string)g_iOnTime; - } else return; - llRegionSayTo(g_kWearer, g_iInterfaceChannel, g_sMessage); - } else if (iNum == LM_SETTING_EMPTY) { - if (sStr == "leash_leashedto") g_iWhoCanChangeLeash=504; - if (sStr == "global_locked") g_iCollarLocked=0; - } else if (iNum == LM_SETTING_RESPONSE) { - list lParams = llParseString2List(sStr, ["="], []); - string sToken = llList2String(lParams, 0); - string sValue = llList2String(lParams, 1); - if (sToken == "global_locked") g_iCollarLocked=(integer)sValue; - else if (sToken == "leash_leashedto") { - g_iWhoCanChangeLeash = (integer)llList2String(llParseString2List(sValue,[","],[]),1); - } - } else if (iNum == MENUNAME_REQUEST && sStr == g_sParentMenu) { - // our parent menu requested to receive buttons, so send ours + + link_message(integer iSender, integer iNum, string sStr, key kID){ + + if (iNum == MENUNAME_REQUEST && sStr == g_sParentMenu) llMessageLinked(iSender, MENUNAME_RESPONSE, g_sParentMenu + "|" + g_sSubMenu, ""); - g_lLocalMenu = [] ; // flush submenu buttons - llMessageLinked(LINK_SET, MENUNAME_REQUEST, g_sSubMenu, ""); - } else if (iNum == MENUNAME_RESPONSE) { - // a button is sned ot be added to a plugin - list lParts = llParseString2List(sStr, ["|"], []); - if (llList2String(lParts, 0) == g_sSubMenu) { - //someone wants to stick something in our menu - string sButton = llList2String(lParts, 1); - if (llListFindList(g_lLocalMenu, [sButton]) == -1) - g_lLocalMenu = llListSort(g_lLocalMenu+[sButton],1,TRUE); + else if (iNum >= CMD_OWNER && iNum <= CMD_EVERYONE) + UserCommand(iNum, sStr, kID); + else if (iNum == DIALOG_TIMEOUT) + { + integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); + g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex +3); //remove stride from g_lMenuIDs + } + else if(iNum == LM_SETTING_RESPONSE) + { + if(sStr=="titler_show=1") g_iTitlerActive=TRUE; + else if(llSubStringIndex(sStr,"titler_color")==0) + { + g_vTitlerColor=(vector)llGetSubString(sStr,13,-1); + llSleep(1); // buffer because titler will clear title + if(LSDRead("TimerActive")=="1" && LSDRead("Titler")=="1" && g_iTitlerActive==FALSE) setText(TRUE); } - } else if (iNum == MENUNAME_REMOVE) { - list lParts = llParseString2List(sStr, ["|"], []); - if (llList2String(lParts, 0) == g_sSubMenu) { - string sButton = llList2String(lParts, 1); - integer iIndex = llListFindList(g_lLocalMenu, [sButton]); - if (~iIndex) g_lLocalMenu = llDeleteSubList(g_lLocalMenu, iIndex, iIndex); + else if(llSubStringIndex(sStr,"titler_offset")==0) + { + g_iTitlerOffset=(integer)llGetSubString(sStr,14,-1); + llSleep(1); // buffer because titler will clear title + if(LSDRead("TimerActive")=="1" && LSDRead("Titler")=="1" && g_iTitlerActive==FALSE) setText(TRUE); } - } else if (iNum == DIALOG_RESPONSE) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - if (iMenuIndex == -1) return; - //this is one of our menus - string sMenu = llList2String(g_lMenuIDs, iMenuIndex+1); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex-1, iMenuIndex-2+g_iMenuStride); - list lMenuParams = llParseString2List(sStr, ["|"], []); - key kAv = (key)llList2String(lMenuParams, 0); - string sMsg = llList2String(lMenuParams, 1); - // integer iPage = (integer)llList2String(lMenuParams, 2); - integer iAuth = (integer)llList2String(lMenuParams, 3); - if (sMenu == "menu") { - if (sMsg == UPMENU) llMessageLinked(LINK_SET, iAuth, "menu "+g_sParentMenu, kAv); - else if (sMsg == "Real Timer") DoRealMenu(kAv, iAuth); - else if (sMsg == "Online Timer") DoOnMenu(kAv, iAuth); - else if (~llListFindList(g_lLocalMenu, [sMsg])) llMessageLinked(LINK_SET, iAuth, "menu "+sMsg, kAv); - else UserCommand(iAuth, "timer " + sMsg, kAv, TRUE); - } else if (sMenu == "real") { - if (sMsg == UPMENU) DoMenu(kAv, iAuth); - else UserCommand(iAuth, "timer real"+sMsg, kAv, TRUE); - } else if (sMenu == "online") { - if (sMsg == UPMENU) DoMenu(kAv, iAuth); - else UserCommand(iAuth, "timer online"+sMsg, kAv, TRUE); - } else if (sMenu == "rmtimer") { - if (sMsg == "Yes") { - llMessageLinked(LINK_ROOT, MENUNAME_REMOVE, g_sParentMenu+"|"+g_sSubMenu, ""); - llMessageLinked(LINK_THIS, NOTIFY, "1"+g_sSubMenu+" App has been removed.", kAv); - if (llGetInventoryType(llGetScriptName()) == INVENTORY_SCRIPT) llRemoveInventory(llGetScriptName()); - } else llMessageLinked(LINK_THIS, NOTIFY, "0"+g_sSubMenu+" App remains installed.", kAv); + } + else if(iNum == LM_SETTING_DELETE) + { + if(sStr=="titler_show") + { + g_iTitlerActive=FALSE; + llSleep(1); // buffer because titler will clear title + if(LSDRead("TimerActive")=="1" && LSDRead("Titler")=="1") setText(TRUE); + } + } + else if(iNum == LSD_REQUEST) llMessageLinked(LINK_SET,LSD_RESPONSE,LSDPrefix,""); + else if(iNum == AUTH_WEARERLOCKOUT) + { + llOwnerSay("You cannot currently access your collar due to wearer lockout being set on an active timer."); + } + else if(iNum == REBOOT && sStr=="reboot") + llMessageLinked(LINK_SET, ALIVE, llGetScriptName(),""); + else if(iNum == READY) + llMessageLinked(LINK_SET, ALIVE, llGetScriptName(), ""); + else if(iNum == STARTUP) + { + g_iCollarActive=TRUE; + if(g_iCatchup) + { + g_iCatchup=FALSE; + stopTimer(TRUE); } - } else if (iNum == DIALOG_TIMEOUT) { + } + else if(iNum == CMD_SAFEWORD && LSDRead("TimerActive")=="1") + { + llOwnerSay("Timer deactivated due to safeword"); + stopTimer(FALSE); + } + else if(iNum == DIALOG_RESPONSE) + { integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - if (~iMenuIndex) g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex-1, iMenuIndex-2+g_iMenuStride); - } else if (iNum == REBOOT && sStr == "reboot") llResetScript(); - } - - timer() - { - g_iCurrentTime=llGetUnixTime(); - if (g_iCurrentTime<(g_iLastRez+60)) return; - if ((g_iCurrentTime-g_iLastTime)<60) g_iOnTime+=g_iCurrentTime-g_iLastTime; - if (g_iOnTime>=g_iFirstOnTime) { - //could store which is need but if both are trigered it will have to send both anyway I prefer not to check for that. - g_sMessage="timer|timeis|"+(string)ON_TIME+"|"+(string)g_iOnTime; - llRegionSayTo(g_kWearer, g_iInterfaceChannel, g_sMessage); - g_iFirstOnTime=MAX_TIME; - g_iTimesLength=llGetListLength(g_lTimes); - for(n = 0; n < g_iTimesLength; n+=2) { - // send notice and find the next time. - if (llList2Integer(g_lTimes, n)==ON_TIME) { - while(llList2Integer(g_lTimes, n+1)<=g_iOnTime&&llList2Integer(g_lTimes, n)==ON_TIME&&g_lTimes!=[]) { - g_lTimes=llDeleteSubList(g_lTimes, n, n+1); - g_iTimesLength=llGetListLength(g_lTimes); - } - if (llList2Integer(g_lTimes, n)==ON_TIME&&llList2Integer(g_lTimes, n+1)=g_iFirstRealTime) { - //could store which is need but if both are trigered it will have to send both anyway I prefer not to check for that. - g_sMessage="timer|timeis|"+(string)REAL_TIME+"|"+(string)g_iCurrentTime; - llRegionSayTo(g_kWearer, g_iInterfaceChannel, g_sMessage); - g_iFirstRealTime=MAX_TIME; - g_iTimesLength=llGetListLength(g_lTimes); - for(n = 0; n < g_iTimesLength; n+=2) { - // send notice and find the next time. - if (llList2Integer(g_lTimes, n)==REAL_TIME) { - while(llList2Integer(g_lTimes, n+1)<=g_iCurrentTime&&llList2Integer(g_lTimes, n)==REAL_TIME) { - g_lTimes=llDeleteSubList(g_lTimes, n, n+1); - g_iTimesLength=llGetListLength(g_lTimes); - } - if (llList2Integer(g_lTimes, n)==REAL_TIME&&llList2Integer(g_lTimes, n+1) must be in form of "token=value" -integer LM_SETTING_REQUEST = 2001; //when startup, scripts send requests for settings on this channel -integer LM_SETTING_RESPONSE = 2002; //the settings script sends responses on this channel -//integer LM_SETTING_DELETE = 2003; //delete token from settings -//integer LM_SETTING_EMPTY = 2004; //sent when a token has no value - -integer DIALOG = -9000; -integer DIALOG_RESPONSE = -9001; -integer DIALOG_TIMEOUT = -9002; - -//------------------------------------// -integer AO_SETTINGS=40500; -integer AO_SETOVERRIDE=40501; -integer AO_GETOVERRIDE=40502; -integer AO_GIVEOVERRIDE=40503; -integer AO_NOTECARD = 40504; -integer AO_STATUS = 40505; -integer AO_ANTISLIDE=40506; -//------------------------------------// -list g_lOptedLM = []; -integer g_iMenuStride; - -string UPMENU = "BACK"; -integer g_iLockAuth=504; - -string g_sDevStage = ""; -string g_sVersion = "2.2"; -integer g_iUpdateAvailable; -key g_kWebLookup; - -key g_kWearer; -string g_sCard = "default"; -integer g_iCardLine; -key g_kCard; -integer g_iReady; - -list g_lAnimStates = [ //http://wiki.secondlife.com/wiki/LlSetAnimationOverride - "Crouching","CrouchWalking","Falling Down","Flying","FlyingSlow", - "Hovering","Hovering Down","Hovering Up","Jumping","Landing", - "PreJumping","Running","Standing","Sitting","Sitting on Ground","Standing Up", - "Striding","Soft Landing","Taking Off","Turning Left","Turning Right","Walking" - ]; - -string g_sJson_Anims = "{}"; -integer g_iAO_ON; -integer g_iSitAnimOn; -string g_sSitAnim; -integer g_iSitAnywhereOn; -string g_sSitAnywhereAnim; -string g_sWalkAnim; -integer g_iChangeInterval = 45; -integer g_iLocked; -integer g_iShuffle; -integer g_iStandPause; - -list g_lMenuIDs; -list g_lAnims2Choose; -list g_lCustomCards; -integer g_iPage; -integer g_iNumberOfPages; -key g_kCollar=NULL_KEY; -integer g_iLMLastRecv; -integer g_iLMLastSent; -/* -Debug(string sStr) { - llScriptProfiler(1); - llOwnerSay(llGetScriptName() + "(min free:"+(string)(llGetMemoryLimit()-llGetSPMaxMemory())+")["+(string)llGetFreeMemory()+"] :\n" + sStr); -}*/ - -//options - - - -// this texture is a spritemap with all buttons on it, for faster texture -// loading than having separate textures for each button. -string BTN_TEXTURE = "fb9a678d-c692-400e-e08c-9e0e85503925"; - -// There are 3 columns of buttons and 8 rows of buttons in the sprite map. -integer BTN_XS = 3; -integer BTN_YS = 2; - -// starting at the top left and moving to the right, the button sprites are in -// this order. -list BTNS = [ - "Minimize", - "Maximize", - "Power", - "Menu", - "SitAny" -]; - -float g_fGap = 0.001; // This is the space between buttons -float g_Yoff = 0.002; // space between buttons and screen top/bottom border -float g_Zoff = 0.04; // space between buttons and screen left/right border - -list g_lButtons ; // buttons names for Order menu -list g_lPrimOrder = [0,1,2,3,4]; // -- List must always start with '0','1' -// -- 0:Spacer, 1:Root, 2:Power, 3:Sit Anywhere, 4:Menu -// -- Spacer serves to even up the list with actual link numbers - -integer g_iLayout = 1; -integer g_iHidden = FALSE; -integer g_iPosition = 69; -integer g_iOldPos; - -vector g_vAOoffcolor = <0.5,0.5,0.5>; -vector g_vAOoncolor = <1,1,1>; - -integer JsonValid(string sTest) { - if (~llSubStringIndex(JSON_FALSE+JSON_INVALID+JSON_NULL,sTest)) - return FALSE; - return TRUE; -} - -FindButtons() { // collect buttons names & links - g_lButtons = [" ", "Minimize"] ; - g_lPrimOrder = [0, 1]; // '1' - root prim - integer i; - for (i=2; i<=llGetNumberOfPrims(); ++i) { - g_lButtons += llGetLinkPrimitiveParams(i, [PRIM_DESC]); - g_lPrimOrder += i; - } -} - -SetButtonTexture(integer link, string name) { - integer idx = llListFindList(BTNS, [name]); - if (idx == -1) return; - integer x = idx % BTN_XS; - integer y = idx / BTN_XS; - vector scale = <1.0 / BTN_XS, 1.0 / BTN_YS, 0>; - vector offset = < - scale.x * (x - (BTN_XS / 2.0 - 0.5)), - scale.y * -1 * (y - (BTN_YS / 2.0 - 0.5)), - 0>; - llSetLinkPrimitiveParamsFast(link, [ - PRIM_TEXTURE, - ALL_SIDES, - BTN_TEXTURE, - scale, - offset, - 0 - ]); -} - -TextureButtons() { - integer i = llGetNumberOfPrims(); - - while (i) { - string name = llGetLinkName(i); - if (i == 1) { - if (g_iHidden) { - name = "Maximize"; - } else { - name = "Minimize"; - } - } - - SetButtonTexture(i, name); - i--; - } -} - -PositionButtons() { - integer iPosition = llGetAttached(); - vector vSize = llGetScale(); -// Allows manual repositioning, without resetting it, if needed - if (iPosition != g_iPosition && iPosition > 30) { //do this only when attached to the hud - vector vOffset = <0.01, vSize.y/2+g_Yoff, vSize.z/2+g_Zoff>; - if (iPosition == ATTACH_HUD_TOP_RIGHT || iPosition == ATTACH_HUD_TOP_CENTER || iPosition == ATTACH_HUD_TOP_LEFT) vOffset.z = -vOffset.z; - if (iPosition == ATTACH_HUD_TOP_LEFT || iPosition == ATTACH_HUD_BOTTOM_LEFT) vOffset.y = -vOffset.y; - llSetPos(vOffset); // Position the Root Prim on screen - g_iPosition = iPosition; - } - if (g_iHidden) { - SetButtonTexture(1, "Maximize"); - llSetLinkPrimitiveParamsFast(LINK_ALL_OTHERS, [PRIM_POSITION,<1,0,0>]); - } else { - SetButtonTexture(1, "Minimize"); - float fYoff = vSize.y + g_fGap; - float fZoff = vSize.z + g_fGap; - if (iPosition == ATTACH_HUD_TOP_LEFT || iPosition == ATTACH_HUD_TOP_CENTER || iPosition == ATTACH_HUD_TOP_RIGHT) - fZoff = -fZoff; - if (iPosition == ATTACH_HUD_TOP_CENTER || iPosition == ATTACH_HUD_TOP_LEFT || iPosition == ATTACH_HUD_BOTTOM || iPosition == ATTACH_HUD_BOTTOM_LEFT) - fYoff = -fYoff; - if (iPosition == ATTACH_HUD_TOP_CENTER || iPosition == ATTACH_HUD_BOTTOM) g_iLayout = 0; - if (g_iLayout) fYoff = 0; - else fZoff = 0; - integer i; - integer LinkCount=llGetListLength(g_lPrimOrder); - for (i=2;i<=LinkCount;++i) { - llSetLinkPrimitiveParamsFast(llList2Integer(g_lPrimOrder,i),[PRIM_POSITION,<0.01, fYoff*(i-1), fZoff*(i-1)>]); - } - } -} - -DoButtonOrder(integer iNewPos) { // -- Set the button order and reset display - integer iOldPos = llList2Integer(g_lPrimOrder,g_iOldPos); - iNewPos = llList2Integer(g_lPrimOrder,iNewPos); - integer i = 2; - list lTemp = [0,1]; - for(;i12) { - lButtons = llDeleteSubList(lButtons,0,0); - lAllButtons = lButtons + lStaticButtons; - } - while (llGetListLength(lAllButtons) % 3 != 0 && llGetListLength(lAllButtons) < 12) { - lSpacers += "-"; - lAllButtons = lButtons + lSpacers + lStaticButtons; - } - integer i = llListFindList(lAllButtons, ["BACK"]); - if (~i) lAllButtons = llDeleteSubList(lAllButtons, i, i); - list lOut = llList2List(lAllButtons, 9, 11); - lOut += llList2List(lAllButtons, 6, 8); - lOut += llList2List(lAllButtons, 3, 5); - lOut += llList2List(lAllButtons, 0, 2); - if (~i) lOut = llListInsertList(lOut, ["BACK"], 2); - return lOut; -} - -MenuAO(key kID,integer iAuth) { - string sPrompt = "\n[OpenCollar AO]\t"+g_sVersion+g_sDevStage; - if (g_iUpdateAvailable) sPrompt+= "\n\nUPDATE AVAILABLE: A new patch has been released.\nPlease install at your earliest convenience. Thanks!"; - list lButtons = ["Load","Sits","Ground Sits","Walks"]; - if (g_iSitAnimOn) lButtons += ["Sits ☑"]; - else lButtons += ["Sits ☐"]; - if (g_iShuffle) lButtons += "Shuffle ☑"; - else lButtons += "Shuffle ☐"; - lButtons += ["Stand Time","Next Stand","Admin Menu"]; - if (kID == g_kWearer) lButtons += "HUD Style"; - Dialog(kID, sPrompt, lButtons, ["Cancel"], iAuth, "AO"); -} - -MenuAdmin(key kID,integer iAuth) { - string sPrompt = "\n[OpenCollar AO]\t"+g_sVersion+g_sDevStage; - list lButtons = ["LOCK"]; - if (g_iLocked) lButtons = ["UNLOCK"]; - else lButtons += "-"; - if (g_kCollar != NULL_KEY) lButtons += ["Collar Menu","DISCONNECT"]; - Dialog(kID, sPrompt, lButtons, ["Cancel","BACK"], iAuth, "Admin"); -} - -MenuLoad(key kID, integer iPage,integer iAuth) { - if (!iPage) g_iPage = 0; - string sPrompt = "\nLoad an animation set!"; - list lButtons; - g_lCustomCards = []; - integer iEnd = llGetInventoryNumber(INVENTORY_NOTECARD); - integer iCountCustomCards; - string sNotecardName; - integer i; - while (i < iEnd) { - sNotecardName = llGetInventoryName(INVENTORY_NOTECARD, i++); - if (llSubStringIndex(sNotecardName,".") && sNotecardName != "") { - if (!llSubStringIndex(sNotecardName,"SET")) - g_lCustomCards += [sNotecardName,"Wildcard "+(string)(++iCountCustomCards)];// + g_lCustomCards; - else if(llStringLength(sNotecardName) < 24) lButtons += sNotecardName; - else llOwnerSay(sNotecardName+"'s name is too long to be displayed in menus and cannot be used."); - } - } - i = 1; - while (i <= 2*iCountCustomCards) { - lButtons += llList2List(g_lCustomCards,i,i); - i += 2; - } - list lStaticButtons = ["BACK"]; - if (llGetListLength(lButtons) > 11) { - lStaticButtons = ["◄","►","BACK"]; - g_iNumberOfPages = llGetListLength(lButtons)/9; - lButtons = llList2List(lButtons,iPage*9,iPage*9+8); - } - if (!llGetListLength(lButtons)) llOwnerSay("There aren't any animation sets installed!"); - Dialog(kID, sPrompt, lButtons, lStaticButtons,iAuth,"Load"); -} - -MenuInterval(key kID,integer iAuth) { - string sInterval = "won't change automatically."; - if (g_iChangeInterval) sInterval = "change every "+(string)g_iChangeInterval+" seconds."; - Dialog(kID, "\nStands " +sInterval, ["Never","20","30","45","60","90","120","180"], ["BACK"],iAuth,"Interval"); -} - -MenuChooseAnim(key kID, string sAnimState, integer iAuth) { - string sAnim = g_sSitAnywhereAnim; - if (sAnimState == "Walking") sAnim = g_sWalkAnim; - else if (sAnimState == "Sitting") sAnim = g_sSitAnim; - string sPrompt = "\n"+sAnimState+": \""+sAnim+"\"\n"; - g_lAnims2Choose = llListSort(llParseString2List(llJsonGetValue(g_sJson_Anims,[sAnimState]),["|"],[]),1,TRUE); - list lButtons; - integer iEnd = llGetListLength(g_lAnims2Choose); - integer i; - while (++i<=iEnd) { - lButtons += (string)i; - sPrompt += "\n"+(string)i+": "+llList2String(g_lAnims2Choose,i-1); - } - Dialog(kID, sPrompt, lButtons, ["BACK"],iAuth,sAnimState); -} - -MenuOptions(key kID,integer iAuth) { - Dialog(kID,"\nCustomize your AO!",["Horizontal","Vertical","Order"],["BACK"],iAuth, "options"); -} - -OrderMenu(key kID,integer iAuth) { - string sPrompt = "\nWhich button do you want to re-order?"; - integer i; - list lButtons; - integer iPos; - for (i=2;iCMD_WEARER) { - if(iNum == AO_SETOVERRIDE){ - if (sCommand == "on") { - g_iAO_ON = TRUE; - llMessageLinked(LINK_THIS,AO_SETTINGS,"iAO_ON="+(string)g_iAO_ON,kID); - llMessageLinked(LINK_THIS,AO_SETTINGS,"start",kID); - ShowStatus(); - } else if (sCommand == "off") { - g_iAO_ON = FALSE; - llMessageLinked(LINK_THIS,AO_SETTINGS,"iAO_ON="+(string)g_iAO_ON,kID); - llMessageLinked(LINK_THIS,AO_SETOVERRIDE,"RESET:ALL",kID); - //llResetAnimationOverride("ALL"); - ShowStatus(); - } else { - return; - } - } - else { - return; - } - } - if (llSubStringIndex(llToLower(sCommand), llToLower(g_sAddon)) && llToLower(sCommand) != "menu " + llToLower(g_sAddon)) return; - if (iNum == CMD_OWNER && llToLower(sCommand) == "runaway") { - llOwnerSay("run away!"); - Command(kID,g_sAddon+"unlock",iNum); - return; - } - - if (llToLower(sCommand) == llToLower(g_sAddon) || llToLower(sCommand) == "menu "+llToLower(g_sAddon)) - { - llOwnerSay("spawn Menu!"); - MenuAO(kID, iNum); - }// else if (iNum!=CMD_OWNER && iNum!=CMD_TRUSTED && kID!=g_kWearer) Notify(kID,"Access denied!",TRUE); - else - { - //integer iWSuccess = 0; - //string sChangetype = llList2String(llParseString2List(sCommand, [" "], []),0); - //string sChangevalue = llList2String(llParseString2List(sCommand, [" "], []),1); - //string sText; - list lParams = llParseString2List(sCommand,[" "],[g_sAddon]); - sCommand = llList2String(lParams,1); - string sValue = llList2String(lParams,2); - if (!g_iReady) { - Notify(kID,"Please load an animation set first.",TRUE); - MenuLoad(kID,0,iNum); - return; - } else if (sCommand == "on") { - g_iAO_ON = TRUE; - llMessageLinked(LINK_THIS,AO_SETTINGS,"iAO_ON="+(string)g_iAO_ON,kID); - llMessageLinked(LINK_THIS,AO_SETTINGS,"start",kID); - ShowStatus(); - } else if (sCommand == "off") { - g_iAO_ON = FALSE; - llMessageLinked(LINK_THIS,AO_SETTINGS,"iAO_ON="+(string)g_iAO_ON,kID); - llMessageLinked(LINK_THIS,AO_SETOVERRIDE,"RESET:ALL",kID); - //llResetAnimationOverride("ALL"); - ShowStatus(); - } else if (sCommand == "unlock"){ - if( iNum <= g_iLockAuth){ - g_iLocked = FALSE; - llOwnerSay("@detach=y"); - llPlaySound("82fa6d06-b494-f97c-2908-84009380c8d1", 1.0); - Notify(kID,"The AO has been unlocked.",TRUE); - } else Notify(kID,"Sorry Authorization denied!",TRUE); - } else if (sCommand == "lock" ){ - if( iNum <= g_iLockAuth) { - g_iLockAuth = iNum; - g_iLocked = TRUE; - llOwnerSay("@detach=n"); - llPlaySound("dec9fb53-0fef-29ae-a21d-b3047525d312", 1.0); - Notify(kID,"The AO has been locked.",TRUE); - }else Notify(kID,"Sorry Authorization denied!",TRUE); - } else if (sCommand == "menu") MenuAO(kID,iNum); - else if (sCommand == "load") { - if (llGetInventoryType(sValue) == INVENTORY_NOTECARD) { - g_sCard = sValue; - g_iCardLine = 0; - g_sJson_Anims = "{}"; - Notify(kID,"Loading animation set \""+g_sCard+"\".",TRUE); - llMessageLinked(LINK_THIS,AO_NOTECARD,g_sCard+"|"+(string)g_iCardLine,kID); - } else MenuLoad(kID,0,iNum); - } - } -} - -PermsCheck() { - string sName = llGetScriptName(); - if (!(llGetObjectPermMask(MASK_OWNER) & PERM_MODIFY)) { - llOwnerSay("You have been given a no-modify OpenCollar object. This could break future updates. Please ask the provider to make the object modifiable."); - } - - if (!(llGetObjectPermMask(MASK_NEXT) & PERM_MODIFY)) { - llOwnerSay("You have put an OpenCollar script into an object that the next user cannot modify. This could break future updates. Please leave your OpenCollar objects modifiable."); - } - - integer FULL_PERMS = PERM_COPY | PERM_MODIFY | PERM_TRANSFER; - if (!((llGetInventoryPermMask(sName,MASK_OWNER) & FULL_PERMS) == FULL_PERMS)) { - llOwnerSay("The " + sName + " script is not mod/copy/trans. This is a violation of the OpenCollar license. Please ask the person who gave you this script for a full-perms replacement."); - } - - if (!((llGetInventoryPermMask(sName,MASK_NEXT) & FULL_PERMS) == FULL_PERMS)) { - llOwnerSay("You have removed mod/copy/trans permissions for the next owner of the " + sName + " script. This is a violation of the OpenCollar license. Please make the script full perms again."); - } -} - - -Link(string packet, integer iNum, string sStr, key kID){ - list packet_data = [ "pkt_type", packet, "iNum", iNum, "addon_name", g_sAddon, "bridge", FALSE, "sMsg", sStr, "kID", kID ]; - - if (packet == "online" || packet == "update") // only add optin if packet type is online or update - { - llListInsertList(packet_data, [ "optin", llDumpList2String(g_lOptedLM, "~") ], -1); - } - - string pkt = llList2Json(JSON_OBJECT, packet_data); - if (g_kCollar != "" && g_kCollar != NULL_KEY) - { - llRegionSayTo(g_kCollar, API_CHANNEL, pkt); - } - else - { - llRegionSay(API_CHANNEL, pkt); - } -} - -initialize(){ - if (llGetInventoryType("oc_installer_sys")==INVENTORY_SCRIPT) return; - g_kWearer = llGetOwner(); - PermsCheck(); - API_CHANNEL = ((integer)("0x" + llGetSubString((string)llGetOwner(), 0, 8))) + 0xf6eb - 0xd2; - llListen(API_CHANNEL, "", "", ""); - FindButtons(); - PositionButtons(); - TextureButtons(); - DetermineColors(); - //g_kCard = llGetNotecardLine(g_sCard, g_iCardLine); - llMessageLinked(LINK_THIS,AO_NOTECARD,g_sCard+"|"+(string)g_iCardLine,g_kWearer); - Link("online", 0, "", llGetOwner()); // This is the signal to initiate communication between the addon and the collar - llSetTimerEvent(5); - g_iLMLastRecv = llGetUnixTime(); -} - -softreset(){ - g_kCollar = NULL_KEY; - API_CHANNEL = ((integer)("0x" + llGetSubString((string)llGetOwner(), 0, 8))) + 0xf6eb - 0xd2; - llListen(API_CHANNEL, "", "", ""); - Link("online", 0, "", llGetOwner()); // This is the signal to initiate communication between the addon and the collar - g_iLMLastRecv = llGetUnixTime(); - FindButtons(); - PositionButtons(); - TextureButtons(); - DetermineColors(); -} - -shutdown(){ - Link("offline", 0, "", llGetOwnerKey(g_kCollar)); - g_lMenuIDs = []; - g_kCollar = NULL_KEY; -} - -default { - state_entry() { - initialize(); - //MenuLoad(g_kWearer,0,CMD_WEARER); - } - - on_rez(integer iStart) { - if (g_kWearer != llGetOwner()) llResetScript(); - if (g_iLocked) llOwnerSay("@detach=n"); - g_iReady = FALSE; - llMessageLinked(LINK_THIS,AO_SETTINGS,"UPDATE",g_kWearer); - llMessageLinked(LINK_THIS,AO_GETOVERRIDE,"all",g_kWearer); - g_kWebLookup = llHTTPRequest("https://raw.githubusercontent.com/OpenCollarTeam/OpenCollar/master/web/ao.txt", [HTTP_METHOD, "GET"],""); - //llRequestPermissions(g_kWearer,PERMISSION_OVERRIDE_ANIMATIONS); - } - - attach(key kID) { - if (kID == NULL_KEY) llMessageLinked(LINK_THIS,AO_SETOVERRIDE,"RESET_ALL",kID);//llResetAnimationOverride("ALL"); - else if (llGetAttached() <= 30) { - llOwnerSay("Sorry, this device can only be attached to the HUD."); - llRequestPermissions(kID, PERMISSION_ATTACH); - llDetachFromAvatar(); - } else { - PositionButtons(); - llMessageLinked(LINK_THIS,AO_GETOVERRIDE,"all",g_kWearer); - llMessageLinked(LINK_THIS,AO_SETTINGS,"UPDATE",g_kWearer); - } - } - - touch_start(integer total_number) { - if(llGetAttached()) { - if (!g_iReady) { - //MenuLoad(g_kWearer,0,CMD_WEARER); - //llOwnerSay("Please load an animation set first."); - llOwnerSay("system not loaded yet!"); - return; - } - string sButton = (string)llGetObjectDetails(llGetLinkKey(llDetectedLinkNumber(0)),[OBJECT_DESC]); - if (sButton == "Menu") - MenuAO(g_kWearer,CMD_WEARER); - else if (sButton == "SitAny") { - ToggleSitAnywhere(); - } else if (llSubStringIndex(llToLower(sButton),"ao")>=0) { - g_iHidden = !g_iHidden; - PositionButtons(); - } else if (sButton == "Power") { - if (g_iAO_ON) Command(g_kWearer,g_sAddon+"off",CMD_WEARER); - else if (g_iReady) Command(g_kWearer,g_sAddon+"on",CMD_WEARER); - } - } else if (llDetectedKey(0) == g_kWearer) MenuAO(g_kWearer,CMD_WEARER); - } - - link_message(integer iSender, integer iNum, string sStr, key kID){ - if( iNum == AO_STATUS ){ - //llOwnerSay(sStr); - list lPar = llParseString2List(sStr, [":","="], []); - string sToken = llList2String(lPar, 0); - string sVar = llList2String(lPar, 1); - string sVal = llList2String(lPar, 2); - if(sToken == "UPDATE"){ - if( sVar == "iSitAnimOn"){ - g_iSitAnimOn = (integer)sVal; - } - else if( sVar == "sWalkAnim"){ - g_sWalkAnim = sVal; - } - else if( sVar == "sSitAnim"){ - g_sSitAnim = sVal; - } - else if( sVar == "iSitAnywhereOn"){ - g_iSitAnywhereOn = (integer)sVal; - } - else if( sVar == "sSitAnywhereAnim"){ - g_sSitAnywhereAnim = sVal; - } - else if(sVar == "iAOOn"){ - g_iAO_ON = (integer)sVal; - } - ShowStatus(); - } - } - if( iNum == AO_GIVEOVERRIDE){ - if(JsonValid(sStr)){ - g_iReady = TRUE; - g_sJson_Anims=sStr; - } - else { - g_iReady = FALSE; - llOwnerSay("Json not valid!"); - } - ShowStatus(); - } - if( iNum == AO_ANTISLIDE){ - llMessageLinked(LINK_THIS,AO_ANTISLIDE,"AOantislide off",g_kWearer); - } - } - - listen(integer iChannel, string sName, key kID, string sMessage) { - if(JsonValid(sMessage)){ - string sPacketType = llJsonGetValue(sMessage, ["pkt_type"]); - if (sPacketType == "approved" && g_kCollar == NULL_KEY) - { - // This signal, indicates the collar has approved the addon and that communication requests will be responded to if the requests are valid collar LMs. - g_kCollar = kID; - g_iLMLastRecv = llGetUnixTime(); - Link("from_addon", LM_SETTING_REQUEST, "ALL", ""); - } - else if (sPacketType == "dc" && g_kCollar == kID) - { - softreset(); - } - else if (sPacketType == "pong" && g_kCollar == kID) - { - g_iLMLastRecv = llGetUnixTime(); - } - else if(sPacketType == "from_collar") - { - // process link message if in range of addon - if (llVecDist(llGetPos(), llList2Vector(llGetObjectDetails(kID, [OBJECT_POS]), 0)) <= 10.0) - { - integer iNum = (integer) llJsonGetValue(sMessage, ["iNum"]); - string sStr = llJsonGetValue(sMessage, ["sMsg"]); - key kID = (key) llJsonGetValue(sMessage, ["kID"]); - if (iNum >= CMD_OWNER && iNum <= CMD_EVERYONE) - { - Command(kID, sStr, iNum); - } - else if ( iNum == AO_SETOVERRIDE){ - Command(kID, sStr, iNum); - } - } - } - } - if (~llListFindList(g_lMenuIDs,[kID, iChannel])) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - integer iAuth = llList2Integer(g_lMenuIDs,iMenuIndex+5); - string sMenuType = llList2String(g_lMenuIDs, iMenuIndex+4); - llListenRemove(llList2Integer(g_lMenuIDs,iMenuIndex+2)); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs,iMenuIndex, iMenuIndex+4); - //if (llGetListLength(g_lMenuIDs) == 0 && (!g_iAO_ON || !g_iChangeInterval)) llSetTimerEvent(0.0); - if (sMenuType == "AO") { - if (sMessage == "Cancel") return; - else if (sMessage == "-") MenuAO(kID,iAuth); - else if (sMessage == "Admin Menu") MenuAdmin(kID,iAuth); - else if (sMessage == "HUD Style") MenuOptions(kID,iAuth); - else if (sMessage == "Load") MenuLoad(kID,0,iAuth); - else if (sMessage == "Sits") MenuChooseAnim(kID,"Sitting",iAuth); - else if (sMessage == "Walks") MenuChooseAnim(kID,"Walking",iAuth); - else if (sMessage == "Ground Sits") MenuChooseAnim(kID,"Sitting on Ground",iAuth); - else if (!llSubStringIndex(sMessage,"Sits")) { - if (~llSubStringIndex(sMessage,"☑")) { - g_iSitAnimOn = FALSE; - llMessageLinked(LINK_THIS,AO_SETTINGS,"iSitAnimOn="+(string)g_iSitAnimOn,kID); - llMessageLinked(LINK_THIS,AO_SETOVERRIDE,"RESET:Sitting",kID); - } else if (g_sSitAnim != "") { - g_iSitAnimOn = TRUE; - if (g_iAO_ON) llMessageLinked(LINK_THIS,AO_SETOVERRIDE,"set:Sitting="+g_sSitAnim,kID); - } else Notify(kID,"Sorry, the currently loaded animation set doesn't have any sits.",TRUE); - MenuAO(kID,iAuth); - } else if (sMessage == "Stand Time") MenuInterval(kID,iAuth); - else if (sMessage == "Next Stand") { - if (g_iAO_ON) llMessageLinked(LINK_THIS,AO_SETOVERRIDE,"switchstand",llGetOwner()); - MenuAO(kID,iAuth); - } else if (!llSubStringIndex(sMessage,"Shuffle")) { - if (~llSubStringIndex(sMessage,"☑")) g_iShuffle = FALSE; - else g_iShuffle = TRUE; - llMessageLinked(LINK_THIS,AO_SETTINGS,"iShuffle="+(string)g_iShuffle,kID); - MenuAO(kID,iAuth); - } - } else if (sMenuType == "Load") { - integer index = llListFindList(g_lCustomCards,[sMessage]); - if (~index) sMessage = llList2String(g_lCustomCards,index-1); - if (llGetInventoryType(sMessage) == INVENTORY_NOTECARD) { - g_sCard = sMessage; - g_iCardLine = 0; - g_sJson_Anims = "{}"; - llMessageLinked(LINK_THIS,AO_NOTECARD,g_sCard+"|"+(string)g_iCardLine,kID); - return; - } else if (g_iReady && sMessage == "BACK") { - MenuAO(kID,iAuth); - return; - } else if (sMessage == "►") { - if (++g_iPage > g_iNumberOfPages) g_iPage = 0; - } else if (sMessage == "◄") { - if (--g_iPage < 0) g_iPage = g_iNumberOfPages; - } else if (!g_iReady) llOwnerSay("Please load an animation set first."); - else llOwnerSay("Could not find animation set: "+sMessage); - MenuLoad(kID,g_iPage,iAuth); - } else if (sMenuType == "Interval") { - if (sMessage == "BACK") { - MenuAO(kID,iAuth); - return; - } else if (sMessage == "Never") { - g_iChangeInterval = FALSE; - llMessageLinked(LINK_THIS,AO_SETTINGS,"iChangeInterval="+(string)g_iChangeInterval,kID); - } else if ((integer)sMessage >= 20) { - g_iChangeInterval = (integer)sMessage; - if (g_iAO_ON && !g_iSitAnywhereOn) llMessageLinked(LINK_THIS,AO_SETTINGS,"iChangeInterval="+(string)g_iChangeInterval,kID); - } - MenuInterval(kID,iAuth); - } else if (~llListFindList(["Walking","Sitting on Ground","Sitting"],[sMenuType])) { - if (sMessage == "BACK") MenuAO(kID,iAuth); - else if (sMessage == "-") MenuChooseAnim(kID,sMenuType,iAuth); - else { - sMessage = llList2String(g_lAnims2Choose,((integer)sMessage)-1); - g_lAnims2Choose = []; - if (llGetInventoryType(sMessage) == INVENTORY_ANIMATION) { - if (sMenuType == "Sitting") { - g_sSitAnim = sMessage; - llMessageLinked(LINK_THIS,AO_SETTINGS,"sSitAnim="+g_sSitAnim,kID); - } - else if (sMenuType == "Sitting on Ground") { - g_sSitAnywhereAnim = sMessage; - llMessageLinked(LINK_THIS,AO_SETTINGS,"sSitAnywhereAnim="+g_sSitAnywhereAnim,kID); - } - else if (sMenuType == "Walking"){ - g_sWalkAnim = sMessage; - llMessageLinked(LINK_THIS,AO_SETTINGS,"sWalkAnim="+g_sWalkAnim,kID); - - } - if (g_iAO_ON && (sMenuType != "Sitting" || g_iSitAnimOn)) llMessageLinked(LINK_THIS,AO_SETOVERRIDE,"set:"+sMenuType+"="+sMessage,kID); - } else llOwnerSay("No "+sMenuType+" animation set."); - MenuChooseAnim(kID,sMenuType,iAuth); - } - } else if (sMenuType == "options") { - if (sMessage == "BACK") { - MenuAO(kID,iAuth); - return; - } else if (sMessage == "Horizontal") { - g_iLayout = 0; - PositionButtons(); - } else if (sMessage == "Vertical") { - g_iLayout = 1; - PositionButtons(); - } else if (sMessage == "Order") { - OrderMenu(kID,iAuth); - return; - } - MenuOptions(kID,iAuth); - } else if (sMenuType == "ordermenu") { - if (sMessage == "BACK") MenuOptions(kID,iAuth); - else if (sMessage == "-") OrderMenu(kID,iAuth); - else if (sMessage == "Reset") { - FindButtons(); - llOwnerSay("Order position reset to default."); - PositionButtons(); - OrderMenu(kID,iAuth); - } else if (llSubStringIndex(sMessage,":") >= 0) { - DoButtonOrder(llList2Integer(llParseString2List(sMessage,[":"],[]),1)); - OrderMenu(kID,iAuth); - } else { - list lButtons; - string sPrompt; - integer iTemp = llListFindList(g_lButtons,[sMessage]); - g_iOldPos = llListFindList(g_lPrimOrder, [iTemp]); - sPrompt = "\nWhich slot do you want to swap for the "+sMessage+" button."; - integer i; - for (i=2;i= (g_iLMLastSent + 30)) - { - g_iLMLastSent = llGetUnixTime(); - Link("ping", 0, "", g_kCollar); - } - - if (llGetUnixTime() > (g_iLMLastRecv + (5 * 60)) && g_kCollar != NULL_KEY) - { - softreset(); - } - - if (g_kCollar == NULL_KEY) Link("online", 0, "", llGetOwner()); - integer n = llGetListLength(g_lMenuIDs)-6; - integer iNow = llGetUnixTime(); - for (n; n>=0; n=n-6) { - integer iDieTime = llList2Integer(g_lMenuIDs,n+3); - if (iNow > iDieTime) { - llListenRemove(llList2Integer(g_lMenuIDs,n+2)); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs,n,n+4); - } - } - } - - http_response(key kRequestID, integer iStatus, list lMeta, string sBody) { - if (kRequestID == g_kWebLookup && iStatus == 200) { - if ((float)sBody > (float)g_sVersion) g_iUpdateAvailable = TRUE; - else g_iUpdateAvailable = FALSE; - } - } - - changed(integer iChange) { - if (iChange & CHANGED_COLOR) { - if (llGetColor(0) != g_vAOoncolor) DetermineColors(); - } else if (iChange & CHANGED_LINK) initialize(); - if (iChange & CHANGED_INVENTORY) { - llMessageLinked(LINK_THIS,AO_NOTECARD,g_sCard+"|"+(string)g_iCardLine,g_kWearer); - softreset(); - PermsCheck(); - } - } -} diff --git a/src/ao/oc_ao_animator b/src/ao/oc_ao_animator deleted file mode 100644 index b5a7af8f2..000000000 --- a/src/ao/oc_ao_animator +++ /dev/null @@ -1,341 +0,0 @@ -// This file is part of OpenCollar. -// Copyright (c) 2008 - 2017 Nandana Singh, Jessenia Mocha, Alexei Maven. Wendy Starfall, -// littlemousy, Romka Swallowtail, Garvin Twine et al. -// Licensed under the GPLv2. See LICENSE for full details. -/*-Authors Attribution- -Taya Maruti - (May 2021) -*/ - - -//integer CMD_ZERO = 0; -//integer CMD_OWNER = 500; -//integer CMD_TRUSTED = 501; -//integer CMD_GROUP = 502; -//integer CMD_WEARER = 503; -//integer CMD_EVERYONE = 504; -//integer CMD_BLOCKED = 598; // <--- Used in auth_request, will not return on a CMD_ZERO -//integer CMD_RLV_RELAY = 507; -//integer CMD_SAFEWORD = 510; -//integer CMD_RELAY_SAFEWORD = 511; -//integer CMD_NOACCESS = 599; -integer AO_SETTINGS=40500; -integer AO_SETOVERRIDE=40501; -integer AO_GETOVERRIDE=40502; -integer AO_GIVEOVERRIDE=40503; -integer AO_NOTECARD = 40504; -integer AO_STATUS = 40505; -integer AO_ANTISLIDE=40506; - -integer CMD_USER; -key g_kWearer; - -list g_lAnimStates = [ //http://wiki.secondlife.com/wiki/LlSetAnimationOverride - "Crouching","CrouchWalking","Falling Down","Flying","FlyingSlow", - "Hovering","Hovering Down","Hovering Up","Jumping","Landing", - "PreJumping","Running","Standing","Sitting","Sitting on Ground","Standing Up", - "Striding","Soft Landing","Taking Off","Turning Left","Turning Right","Walking" - ]; - -string g_sJson_Anims = "{}"; -string g_sWalkAnim; -integer g_iSitAnimOn; -string g_sSitAnim; -integer g_iSitAnywhereOn; -string g_sSitAnywhereAnim; -integer g_iShuffle; -integer g_iStandPause; -integer g_iChangeInterval = 45; -integer g_iReady; -integer g_iAO_ON; -integer g_iCardLine; -key g_kCard; -string g_sCard = "Default"; -list g_lCustomCards; - -//ao functions - -integer JsonValid(string sTest) { - if (~llSubStringIndex(JSON_FALSE+JSON_INVALID+JSON_NULL,sTest)) - return FALSE; - return TRUE; -} - -SetAnimOverride() { - if (llGetPermissions() & PERMISSION_OVERRIDE_ANIMATIONS) { - llResetAnimationOverride("ALL"); - integer i = 22; //llGetListLength(g_lAnimStates); - string sAnim; - string sAnimState; - do { - sAnimState = llList2String(g_lAnimStates,i); - if (~llSubStringIndex(g_sJson_Anims,sAnimState)) { - sAnim = llJsonGetValue(g_sJson_Anims,[sAnimState]); - if (JsonValid(sAnim)) { - if (sAnimState == "Walking" && g_sWalkAnim != "") - sAnim = g_sWalkAnim; - else if (sAnimState == "Sitting" && !g_iSitAnimOn) jump next; - else if (sAnimState == "Sitting" && g_sSitAnim != "" && g_iSitAnimOn) - sAnim = g_sSitAnim; - else if (sAnimState == "Sitting on Ground" && g_sSitAnywhereAnim != "") - sAnim = g_sSitAnywhereAnim; - else if (sAnimState == "Standing") - sAnim = llList2String(llParseString2List(sAnim, ["|"],[]),0); - if (llGetInventoryType(sAnim) == INVENTORY_ANIMATION) - llSetAnimationOverride(sAnimState, sAnim); - else llOwnerSay(sAnim+" could not be found."); - @next; - } - } - } while (i--); - llSetTimerEvent(g_iChangeInterval); - if (!g_iStandPause) llMessageLinked(LINK_THIS,AO_ANTISLIDE,(string)g_kWearer+":antislide off ao",llGetOwner()); - //llOwnerSay("AO ready ("+(string)llGetFreeMemory()+" bytes free memory)"); - } -} - -SwitchStand() { - if (g_iStandPause) return; - if (llGetPermissions() & PERMISSION_OVERRIDE_ANIMATIONS) { - string sCurAnim = llGetAnimationOverride("Standing"); - list lAnims = llParseString2List(llJsonGetValue(g_sJson_Anims,["Standing"]),["|"],[]); - integer index; - if (g_iShuffle) index = (integer)llFrand(llGetListLength(lAnims)); - else { - index = llListFindList(lAnims,[sCurAnim]); - if (index == llGetListLength(lAnims)-1) index = 0; - else index += 1; - } - if (g_iReady) llSetAnimationOverride("Standing",llList2String(lAnims,index)); - } -} - -PermsCheck() { - string sName = llGetScriptName(); - if (!(llGetObjectPermMask(MASK_OWNER) & PERM_MODIFY)) { - llOwnerSay("You have been given a no-modify OpenCollar object. This could break future updates. Please ask the provider to make the object modifiable."); - } - - if (!(llGetObjectPermMask(MASK_NEXT) & PERM_MODIFY)) { - llOwnerSay("You have put an OpenCollar script into an object that the next user cannot modify. This could break future updates. Please leave your OpenCollar objects modifiable."); - } - - integer FULL_PERMS = PERM_COPY | PERM_MODIFY | PERM_TRANSFER; - if (!((llGetInventoryPermMask(sName,MASK_OWNER) & FULL_PERMS) == FULL_PERMS)) { - llOwnerSay("The " + sName + " script is not mod/copy/trans. This is a violation of the OpenCollar license. Please ask the person who gave you this script for a full-perms replacement."); - } - - if (!((llGetInventoryPermMask(sName,MASK_NEXT) & FULL_PERMS) == FULL_PERMS)) { - llOwnerSay("You have removed mod/copy/trans permissions for the next owner of the " + sName + " script. This is a violation of the OpenCollar license. Please make the script full perms again."); - } -} - -default { - state_entry() { - g_kWearer = llGetOwner(); - PermsCheck(); - } - - link_message(integer iSender, integer iNum, string sStr, key kID) { - if( iNum == AO_SETTINGS){ - list lPar = llParseString2List(sStr, ["=","|"], []); - string sVar = llList2String(lPar, 0); - string sVal = llList2String(lPar, 1); - string sVal2 = llList2String(lPar, 2); - if( sVar == "UPDATE"){ - SetAnimOverride(); - } - else if( sVar == "iSitAnimOn"){ - g_iSitAnimOn = (integer)sVal; - } - else if( sVar == "sWalkAnim"){ - g_sWalkAnim = sVal; - SetAnimOverride(); - } - else if( sVar == "iSitAnywhereOn"){ - g_iSitAnywhereOn = (integer)sVal; - } - else if( sVar == "sString_Anim"){ - g_sSitAnim = sVal; - } - else if( sVar == "sSitAnywhereAnim"){ - g_sSitAnywhereAnim = sVal; - } - else if( sVar == "iShuffle"){ - g_iShuffle = (integer)sVal; - } - else if( sVar == "iStandPause"){ - g_iStandPause = (integer)sVal; - } - else if( sVar == "iChangeInterval"){ - g_iChangeInterval = (integer)sVal; - llSetTimerEvent(g_iChangeInterval); - } - else if(sVar == "iAO_ON"){ - g_iAO_ON = (integer)sVal; - if(g_iAO_ON){ - llRequestPermissions(g_kWearer,PERMISSION_OVERRIDE_ANIMATIONS); - SetAnimOverride(); - } - else { - llSetTimerEvent(0); - //llResetAnimationOverride("ALL"); - } - } - } - else if( iNum == AO_SETOVERRIDE){ - list lPar = llParseString2List(sStr, [":","="], []); - string sToken = llList2String(lPar, 0); - string sVar = llList2String(lPar, 1); - string sVal = llList2String(lPar, 2); - if(sToken == "switchstand"){ - SwitchStand(); - } - else if(sToken == "set"){ - llSetAnimationOverride(sVar,sVal); - } - else if(sToken == "RESET"){ - llResetAnimationOverride(llToUpper(sVar)); - } - } - else if( iNum == AO_GETOVERRIDE){ - if( sStr == "" | sStr == "all"){ - llMessageLinked(LINK_THIS,AO_GIVEOVERRIDE,g_sJson_Anims,kID); - } - else if(~llSubStringIndex(g_sJson_Anims,sStr)){ - list lAnims = llParseString2List(llJsonGetValue(g_sJson_Anims,[sStr]),["|"],[]); - llMessageLinked(LINK_THIS,AO_GIVEOVERRIDE,"{"+sStr+llDumpList2String(lAnims,"|")+"}",kID); - } - else { - llMessageLinked(LINK_THIS,AO_GIVEOVERRIDE,"404",kID); - } - } - else if( iNum == AO_NOTECARD){ - list lPar = llParseString2List(sStr, ["|"], []); - string sVal = llList2String(lPar, 0); - integer iVal = llList2Integer(lPar, 1); - if(llGetInventoryType(sVal) == INVENTORY_NOTECARD){ - if(iVal == 0){ - g_sJson_Anims = "{}"; - } - g_sCard = sVal; - g_iCardLine = iVal; - g_kCard = llGetNotecardLine(g_sCard, g_iCardLine); - } - } - } - - timer() { - if (g_iAO_ON && g_iChangeInterval) SwitchStand(); - } - - run_time_permissions(integer iFlag) { - if (iFlag & PERMISSION_OVERRIDE_ANIMATIONS) { - if (g_sJson_Anims != "{}") g_iReady = TRUE; - else g_iReady = FALSE; - if (g_iAO_ON) SetAnimOverride(); - else llResetAnimationOverride("ALL"); - } - } - - on_rez(integer start_pram){ - if(g_iAO_ON){ - llRequestPermissions(g_kWearer,PERMISSION_OVERRIDE_ANIMATIONS); - SwitchStand(); - } - } - - changed(integer iChange) { - if (iChange & CHANGED_LINK) { - if(g_iReady){ - if(g_iAO_ON){ - llRequestPermissions(g_kWearer,PERMISSION_OVERRIDE_ANIMATIONS); - SwitchStand(); - } - } - } - if (iChange & CHANGED_INVENTORY) PermsCheck(); - } - - - dataserver(key kRequest, string sData) { - if (kRequest == g_kCard) { - if (sData != EOF) { - if (llGetSubString(sData,0,0) != "[") jump next; - string sAnimationState = llStringTrim(llGetSubString(sData,1,llSubStringIndex(sData,"]")-1),STRING_TRIM); - // Translate common ZHAOII, Oracul and AX anim state values - if (sAnimationState == "Stand.1" || sAnimationState == "Stand.2" || sAnimationState == "Stand.3") sAnimationState = "Standing"; - else if (sAnimationState == "Walk.N") sAnimationState = "Walking"; - else if (sAnimationState == "Running") sAnimationState = "Running"; - else if (sAnimationState == "Turn.L") sAnimationState = "Turning Left"; - else if (sAnimationState == "Turn.R") sAnimationState = "Turning Right"; - else if (sAnimationState == "Sit.N") sAnimationState = "Sitting"; - else if (sAnimationState == "Sit.G" || sAnimationState == "Sitting On Ground") sAnimationState = "Sitting on Ground"; - else if (sAnimationState == "Crouch") sAnimationState = "Crouching"; - else if (sAnimationState == "Walk.C" || sAnimationState == "Crouch Walking") sAnimationState = "CrouchWalking"; - else if (sAnimationState == "Jump.N" || sAnimationState == "Jumping") sAnimationState = "Jumping"; - //else if (sAnimationState == "Takeoff") sAnimationState = "Taking Off"; - else if (sAnimationState == "Hover.N") sAnimationState = "Hovering"; - else if (sAnimationState == "Hover.U" || sAnimationState == "Flying Up") sAnimationState = "Hovering Up"; - else if (sAnimationState == "Hover.D" || sAnimationState == "Flying Down") sAnimationState = "Hovering Down"; - else if (sAnimationState == "Fly.N") sAnimationState = "Flying"; - else if (sAnimationState == "Flying Slow") sAnimationState = "FlyingSlow"; - else if (sAnimationState == "Land.N") sAnimationState = "Landing"; - else if (sAnimationState == "Falling") sAnimationState = "Falling Down"; - /* ----------------------------------------------------------------------- */ - // Meeded for competition events like those held in pony play - else if (sAnimationState == "Striding") sAnimationState = "Striding"; - else if (sAnimationState == "Soft Landing") sAnimationState = "Soft Landing"; - else if (sAnimationState == "Jump.P" || sAnimationState == "Pre Jumping") sAnimationState = "PreJumping"; - else if (sAnimationState == "Stand.U") sAnimationState = "Standing Up"; - /* ----------------------------------------------------------------------- */ - if (!~llListFindList(g_lAnimStates,[sAnimationState])) jump next; - if (llStringLength(sData)-1 > llSubStringIndex(sData,"]")) { - sData = llGetSubString(sData,llSubStringIndex(sData,"]")+1,-1); - list lTemp = llParseString2List(sData, ["|",","],[]); - integer i = llGetListLength(lTemp); - while(i--) { - if (llGetInventoryType(llList2String(lTemp,i)) != INVENTORY_ANIMATION) - lTemp = llDeleteSubList(lTemp,i,i); - } - if (sAnimationState == "Sitting on Ground"){ - g_sSitAnywhereAnim = llList2String(lTemp,0); - llMessageLinked(LINK_THIS,AO_STATUS,"UPDATE:sSitAnywhereAnim="+g_sSitAnywhereAnim,llGetOwner()); - } - else if (sAnimationState == "Sitting") { - g_sSitAnim = llList2String(lTemp,0); - llMessageLinked(LINK_THIS,AO_STATUS,"UPDATE:sSitAnim="+g_sSitAnim,llGetOwner()); - if (g_sSitAnim != "") g_iSitAnimOn = TRUE; - else g_iSitAnimOn = FALSE; - llMessageLinked(LINK_THIS,AO_STATUS,"UPDATE:iSitAnimOn="+(string)g_iSitAnimOn,llGetOwner()); - } else if (sAnimationState == "Walking"){ - g_sWalkAnim = llList2String(lTemp,0); - llMessageLinked(LINK_THIS,AO_STATUS,"UPDATE:sWaLkAnim="+g_sWalkAnim,llGetOwner()); - } - else if (sAnimationState != "Standing") lTemp = llList2List(lTemp,0,0); - if (lTemp) g_sJson_Anims = llJsonSetValue(g_sJson_Anims, [sAnimationState],llDumpList2String(lTemp,"|")); - } - @next; - g_kCard = llGetNotecardLine(g_sCard,++g_iCardLine); - } else { - g_iCardLine = 0; - g_kCard = ""; - g_iSitAnywhereOn = FALSE; - llMessageLinked(LINK_THIS,AO_STATUS,"UPDATE:iSitAnywhereOn="+(string)g_iSitAnywhereOn,llGetOwner()); - integer index = llListFindList(g_lCustomCards,[g_sCard]); - if (~index) g_sCard = llList2String(g_lCustomCards,index+1)+" ("+g_sCard+")"; - g_lCustomCards = []; - if (g_sJson_Anims == "{}") { - llOwnerSay("\""+g_sCard+"\" is an invalid animation set and can't play."); - g_iAO_ON = FALSE; - llMessageLinked(LINK_THIS,AO_STATUS,"UPDATE:iAO_ON="+(string)g_iAO_ON,llGetOwner()); - } else { - llOwnerSay("The \""+g_sCard+"\" animation set was loaded successfully."); - g_iAO_ON = TRUE; - llMessageLinked(LINK_THIS,AO_GIVEOVERRIDE,g_sJson_Anims,llGetOwner()); - llMessageLinked(LINK_THIS,AO_STATUS,"UPDATE:iAOOn="+(string)g_iAO_ON,llGetOwner()); - } - llRequestPermissions(g_kWearer,PERMISSION_OVERRIDE_ANIMATIONS); - } - } - } -} diff --git a/src/collar/oc_addons.lsl b/src/collar/oc_addons.lsl index 830152273..8a06b1b70 100644 --- a/src/collar/oc_addons.lsl +++ b/src/collar/oc_addons.lsl @@ -9,12 +9,13 @@ Medea (medea.destiny) Sept 2021 - Provided auth failure mode for menu. Insufficient auth now provides suitable notification to user and respawns main menu. Fixes issue #665 - KristenMynx Dec 2021 - Fix timeout removal stride May 2022 - Fix offline removal stride - Reduce chatter Jun 2022 - Reduce chatter more +Ping (pingout.duffield) + April 2024 - Fix at Initial Handshake to disregard any nearby rezzed collar. Ref Issue #1038 @@ -26,7 +27,7 @@ https://github.com/OpenCollarTeam/OpenCollar string g_sParentMenu = "Main"; string g_sSubMenu = "Addons"; -string COLLAR_VERSION = "8.2.3000"; +string COLLAR_VERSION = "8.3.0000"; string Auth2Str(integer iAuth){ if(iAuth == CMD_OWNER)return "Owner"; @@ -330,6 +331,7 @@ state active } else if(PacketType == "from_collar")return; // We should never listen to another collar's LMs, wearer should not be wearing more than one anyway. else if(PacketType == "online"){ // this is a initial handshake + if(llJsonGetValue(m,["addon_name"])=="OpenCollar") return; if(llJsonGetValue(m,["kID"])==(string)llGetOwner()){ // begin to pass stuff to link messages! @@ -558,4 +560,3 @@ state active //llOwnerSay(llDumpList2String([iSender,iNum,sStr,kID],"^")); } } - diff --git a/src/collar/oc_anim.lsl b/src/collar/oc_anim.lsl index 578901be7..290aaaa76 100644 --- a/src/collar/oc_anim.lsl +++ b/src/collar/oc_anim.lsl @@ -21,6 +21,13 @@ K9K8E Tayaphidoux Jun 2022 - Restore AO pause functionality +Medea (Medea Destiny) + Nov 2023 - Added StandOffset checkbox button to Anim menu. This toggles use of hover hieght + offset when no pose is selected (i.e. for stand animation). This has been a frequent + problem in support, with people accidentally setting a stand offset. However as some + people may use this it seemed better to keep the capability but have it switchable and + default to off. Issue #1012 + Licensed under the GPLv2. See LICENSE for full details. https://github.com/OpenCollarTeam/OpenCollar */ @@ -110,12 +117,12 @@ integer bool(integer a){ else return FALSE; } integer g_iAnimLock=FALSE; -list g_lCheckboxes=["▢", "▣"]; +list g_lCheckboxes= ["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; } integer g_iPosture = FALSE; - +integer g_iStandOffset = FALSE; Menu(key kID, integer iAuth) { string sPrompt = "\n[Animations]\n\nCurrent Animation: "+setor((g_lCurrentAnimations==[]), "None", llList2String(g_lCurrentAnimations, 0)+"\nCurrent Pose: "+setor((g_sPose==""), "None", g_sPose)); list lButtons = [Checkbox(g_iAnimLock,"AnimLock"), "Pose"]; @@ -125,6 +132,8 @@ Menu(key kID, integer iAuth) { }else { sPrompt += "\n\n* Posture is only available if you have the ~stiff animation in inventory of the collar"; } + lButtons +=[Checkbox(g_iStandOffset, "StandOffset")]; + sPrompt+="\nHeight offsets while no pose is active can be toggled on or off with 'StandOffset;"; Dialog(kID, sPrompt, lButtons+g_lAdditionalButtons, [UPMENU], 0, iAuth, "Menu~Animations"); } @@ -216,21 +225,23 @@ UserCommand(integer iNum, string sStr, key kID) { iRespringPoses=TRUE; } else if(sChangetype == UP_ARROW || sChangetype == "up" || sChangetype == DOWN_ARROW || sChangetype == "down"){ // only owner or wearer - if(iNum == CMD_OWNER || iNum == CMD_WEARER){ + if(iNum == CMD_OWNER || kID == g_kWearer){ // adjust current pose //llOwnerSay(" up or down"); //sChangevalue="remenu"; integer iUp= FALSE; iRespringPoses=TRUE; if(sChangetype == UP_ARROW || sChangetype == "up")iUp=TRUE; - if(g_lCurrentAnimations == []){ + if(g_lCurrentAnimations == [] ){ // adjust standing //llOwnerSay("up: "+(string)iUp+"; anims list blank"); - if(iUp)g_fStandHover += g_fAdjustment; - else g_fStandHover-=g_fAdjustment; - if(g_fStandHover==0)llMessageLinked(LINK_SET,LM_SETTING_DELETE,"offset_standhover",""); - else llMessageLinked(LINK_SET, LM_SETTING_SAVE, "offset_standhover="+(string)g_fStandHover,""); - llMessageLinked(LINK_SET, NOTIFY, "0The hover height for 'Standing' is now "+(string)g_fStandHover, g_kWearer); + if(g_iStandOffset == TRUE) { + if(iUp)g_fStandHover += g_fAdjustment; + else g_fStandHover-=g_fAdjustment; + if(g_fStandHover==0)llMessageLinked(LINK_SET,LM_SETTING_DELETE,"offset_standhover",""); + else llMessageLinked(LINK_SET, LM_SETTING_SAVE, "offset_standhover="+(string)g_fStandHover,""); + llMessageLinked(LINK_SET, NOTIFY, "0The hover height for 'Standing' is now "+(string)g_fStandHover, g_kWearer); + } else llMessageLinked(LINK_SET,NOTIFY,"0Hover height for 'Standing' cannot be set while StandOffset is off.",kID); } else { integer iPos=llListFindList(g_lAdjustments,llList2List(g_lCurrentAnimations, 0, 0)); if(iPos==-1){ @@ -268,7 +279,20 @@ UserCommand(integer iNum, string sStr, key kID) { } - }else llMessageLinked(LINK_SET, NOTIFY, "%NOACCESS% to changing height", kID); + }else llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to changing pose offset", kID); + } else if(sChangetype=="standoffset"){ + if(iNum==CMD_OWNER || kID==g_kWearer) { + if(sChangevalue=="on") { + g_iStandOffset=TRUE; + llMessageLinked(LINK_SET,LM_SETTING_SAVE, "offset_standoffset=1",""); + } else { + g_iStandOffset=FALSE; + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "offset_standoffset",""); + } + + + llMessageLinked(LINK_SET,NOTIFY,"1Stand Offset is now "+llList2String(["off","on"],g_iStandOffset),kID); + }else llMessageLinked(LINK_SET,NOTIFY,"0%NOACCESS%% to changing stand offset",kID); } else if(sChangetype == "animlock"){ string text; if(iNum == CMD_OWNER){ @@ -344,7 +368,7 @@ MoveEnd(){ if(g_iLeashMove)return; if(g_iPermissionGranted){ if(g_lCurrentAnimations==[]){ - if (g_fStandHover != 0.0) + if (g_fStandHover != 0.0 && g_iStandOffset == TRUE) llMessageLinked(LINK_SET, RLV_CMD, "adjustheight:1;0;"+(string)g_fStandHover+"=force",g_kWearer); }else{ g_iTimerMode = TIMER_START_ANIMATION; @@ -386,7 +410,7 @@ StopAnimation(string anim){ g_lCurrentAnimations = llDeleteSubList(g_lCurrentAnimations, aPos, aPos); if (aPos == 0){ if (g_lCurrentAnimations == []){ - if(g_fStandHover!=0)llMessageLinked(LINK_SET,RLV_CMD, "adjustheight:1;0;"+(string)g_fStandHover+"=force", g_kWearer); + if(g_fStandHover!=0 && g_iStandOffset == TRUE )llMessageLinked(LINK_SET,RLV_CMD, "adjustheight:1;0;"+(string)g_fStandHover+"=force", g_kWearer); else llMessageLinked(LINK_SET, RLV_CMD, "adjustheight:1;0;0=force",g_kWearer); }else PlayAnimation(); } @@ -397,7 +421,7 @@ StopAllAnimations(){ llStopAnimation(llList2String(g_lCurrentAnimations, 0)); g_lCurrentAnimations = []; g_sPose = ""; - if(g_fStandHover!=0)llMessageLinked(LINK_SET,RLV_CMD, "adjustheight:1;0;"+(string)g_fStandHover+"=force", g_kWearer); + if(g_fStandHover!=0 && g_iStandOffset == TRUE )llMessageLinked(LINK_SET,RLV_CMD, "adjustheight:1;0;"+(string)g_fStandHover+"=force", g_kWearer); else llMessageLinked(LINK_SET, RLV_CMD, "adjustheight:1;0;0=force",g_kWearer); } @@ -505,7 +529,7 @@ state active if(g_iTimerMode == TIMER_START_ANIMATION && llGetTime()>2.5){ if(g_lCurrentAnimations==[]){ - if(g_fStandHover != 0) llMessageLinked(LINK_SET, RLV_CMD, "adjustheight:1;0;"+(string)g_fStandHover,g_kWearer); + if(g_fStandHover != 0 && g_iStandOffset==TRUE) llMessageLinked(LINK_SET, RLV_CMD, "adjustheight:1;0;"+(string)g_fStandHover,g_kWearer); }else PlayAnimation(); llSetTimerEvent(FALSE); } @@ -570,15 +594,22 @@ state active else if(sMsg == "Pose"){ PoseMenu(kAv,iAuth, 0); iRespring=FALSE; + } else if(sMsg == Checkbox(g_iStandOffset,"StandOffset")){ + if(iAuth==CMD_OWNER || kAv==g_kWearer) { + g_iStandOffset=!g_iStandOffset; + if(g_iStandOffset) UserCommand(iAuth,"standoffset on",kAv); + else UserCommand(iAuth,"standoffset off",kAv); + } + else llMessageLinked(LINK_SET,NOTIFY,"0%NOACCESS% to changing stand offset",kAv); } else if(sMsg == Checkbox(g_iPosture,"Posture")){ if(iAuth == CMD_OWNER){ if(g_iPosture){ g_iPosture=0; - llMessageLinked(LINK_SET, 0, "posture off",kAv); + llMessageLinked(LINK_SET, iAuth, "posture off",kAv); } else { g_iPosture=1; - llMessageLinked(LINK_SET, 0, "posture on",kAv); + llMessageLinked(LINK_SET, iAuth, "posture on",kAv); } } @@ -635,12 +666,18 @@ state active RunPosture(); } } else if(sTok == "offset"){ - if(sVar == "hovers"){ + if(sVar=="standoffset") { + g_iStandOffset=(integer)sVal; + if(g_lCurrentAnimations == [] && g_fStandHover!=0)llMessageLinked(LINK_SET,RLV_CMD, "adjustheight:1;0;"+(string)g_fStandHover+"=force", g_kWearer); + } else if(sVar == "hovers"){ g_lAdjustments = llParseString2List(sVal,[","],[]); } else if(sVar == "standhover"){ - g_fStandHover = (float)sVal; - if(g_fStandHover!=0)llMessageLinked(LINK_SET,RLV_CMD, "adjustheight:1;0;"+(string)g_fStandHover+"=force", g_kWearer); - else llMessageLinked(LINK_SET, RLV_CMD, "adjustheight:1;0;0=force",g_kWearer); + float fNew = (float)sVal; + if(g_lCurrentAnimations == []){ + if(fNew!=0 && g_iStandOffset)llMessageLinked(LINK_SET,RLV_CMD, "adjustheight:1;0;"+(string)fNew+"=force", g_kWearer); + else if(g_fStandHover!=0) llMessageLinked(LINK_SET, RLV_CMD, "adjustheight:1;0;0=force",g_kWearer); + } + g_fStandHover=fNew; } } } else if(iNum == LM_SETTING_DELETE){ @@ -667,7 +704,10 @@ state active RunPosture(); } } else if(sTok == "offset"){ - if(sVar == "hovers"){ + if(sVar =="standoffset") { + g_iStandOffset=FALSE; + if(g_lCurrentAnimations == []) llMessageLinked(LINK_SET, RLV_CMD, "adjustheight:1;0;0=force",g_kWearer); + } else if(sVar == "hovers"){ g_lAdjustments=[]; } else if(sVar == "standhover"){ g_fStandHover = 0; diff --git a/src/collar/oc_api.lsl b/src/collar/oc_api.lsl index 5e07f34f6..fb4f8cf67 100644 --- a/src/collar/oc_api.lsl +++ b/src/collar/oc_api.lsl @@ -1,3 +1,4 @@ + /* This file is a part of OpenCollar. Copyright ©2021 @@ -19,9 +20,47 @@ Medea (Medea Destiny) blacklist when permitted when also trusted (issue #849) - Fixed instance where interface channel is 0, also streamlined function, fix issue #819 - Prefix reset now works, and notifications sent when changing prefix. + *Nov 2023 - Reformatted chat command handling for efficiency. Chat commands no longer sent as + CMD_ZERO for authing as this script handles + auth so we can auth locally to save a link_message round trip on every chat command. + - Restored object command handling as per v7.x and previous, using interface channel + rather than HUDchannel as there seems no reason to duplicate and hudchannel stuff hasn't + worked for a few years anyway. Added remote auth function - + llSay(g_iInterfaceChannel,"checkauth 1111"); will return "AuthReply|(wearerkey)|(auth level)" + on channel 1111, reporting the auth level of the object owner. Commands can be prefixed with + "authas:(userkey)=", which will use the LOWER auth level between object owner and userkey. + Commmand format is targetkey:chat command. Examples: + "authas:(userkey)=(targetkey):kneel" - will issue kneel command if (userkey) and object owner + both have valid auth + "(targetkey):sit (sittarget key)" - sit wearer on (sittarget key) if object owner has valid + auth + - menuto cleanup requires menuto target to be in sim AND be the owner of the issuing command + - Set g_iStartup to TRUE in active state on_rez event to restore "owned by" message #906 + *May 2024 - Implemented wearerlockout for timer app and future rework of capture app, as well + as future options for self-bondage, excluding wearer from all collar access etc. This uses + Linkset data, the token being 'auth_WearerLockout". Any script that initiates a lockout + should add its script name (excluding the "oc_" part to a comma-separated list, and remove + itself from that list when removing, so that multiple scripts can apply this without interfering + with each other. + When llLinksetDataRead("auth_WearerLocout") does not return an empty string, + the only thing the wearer can do is safeword, which will deactivate it. + Any attempt to trigger a menu/command will send the AUTH_WEARERLOCKOUT + Link message. Any script setting a wearer lockout should respond to this with a status + update. + Yosty7b3 *Oct 2021 - Remove unused StrideOfList() function. - *Feb 2022 - Only reset when needed (part of boot speedup project). + + *Feb 2022 - Only reset when needed (part of boot speedup project). + +Nikki Lacrima + *Aug 2023 - Clear group on runaway issue #935 + *Nov 2023 - Only wearer can initiate and confirm runaway, add link message "runaway_confirmed" and + remove the g_iRunawayMode. + - Add "#" prefix wildcard, issue #897 + - implemented Yosty7b3's menu streamlining, see pr#963 + + et al. Licensed under the GPLv2. See LICENSE for full details. https://github.com/OpenCollarTeam/OpenCollar @@ -45,16 +84,10 @@ integer ACTION_OWNER = 8; integer ACTION_TRUST = 16; integer ACTION_BLOCK = 32; -//integer g_iLastGranted; -//key g_kLastGranted; -//string g_sLastGranted; - //integer TIMEOUT_READY = 30497; integer TIMEOUT_REGISTER = 30498; integer TIMEOUT_FIRED = 30499; - - //integer RLV_CMD = 6000; integer RLV_REFRESH = 6001;//RLV plugins should reinstate their restrictions upon receiving this message. @@ -63,6 +96,7 @@ integer RLV_REFRESH = 6001;//RLV plugins should reinstate their restrictions upo //MESSAGE MAP integer AUTH_REQUEST = 600; integer AUTH_REPLY=601; +integer AUTH_WEARERLOCKOUT=602; integer CMD_ZERO = 0; integer CMD_OWNER = 500; @@ -100,12 +134,7 @@ integer LM_SETTING_DELETE = 2003;//delete token from settings //integer LM_SETTING_EMPTY = 2004;//sent when a token has no value Dialog(key kID, string sPrompt, list lChoices, list lUtilityButtons, integer iPage, integer iAuth, string sName) { - key kMenuID = llGenerateKey(); - llMessageLinked(LINK_SET, DIALOG, (string)kID + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lChoices, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, kMenuID); - - integer iIndex = llListFindList(g_lMenuIDs, [kID]); - if (~iIndex) g_lMenuIDs = llListReplaceList(g_lMenuIDs, [kID, kMenuID, sName], iIndex, iIndex + g_iMenuStride - 1); - else g_lMenuIDs += [kID, kMenuID, sName]; + llMessageLinked(LINK_SET, DIALOG, (string)kID + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lChoices, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, sName+"~"+llGetScriptName() ); } string SLURL(key kID){ return "secondlife:///app/agent/"+(string)kID+"/about"; @@ -118,6 +147,7 @@ key g_kMenuUser; integer CalcAuth(key kID) { + if(llLinksetDataRead("auth_WearerLockout")!="" && kID==g_kWearer) return CMD_NOACCESS; string sID = (string)kID; // First check if(llGetListLength(g_lOwner) == 0 && kID==g_kWearer && llListFindList(g_lBlock,[sID])==-1) @@ -144,9 +174,6 @@ integer CalcAuth(key kID) { return CMD_NOACCESS; } -list g_lMenuIDs; -integer g_iMenuStride; - integer NOTIFY = 1002; integer NOTIFY_OWNERS=1003; integer g_iPublic; @@ -194,25 +221,28 @@ DoListeners(){ integer g_iRunaway=TRUE; key g_kDenyRunawayRequester; + RunawayMenu(key kID, integer iAuth){ - if(iAuth == CMD_OWNER || kID == g_kWearer){ - string sPrompt = "\n[Runaway]\n\nAre you sure you want to runaway from all owners?\n\n* This action will reset your owners list, trusted list, and your blocked avatars list."; - list lButtons = ["Yes", "No"]; - + if(iAuth == CMD_OWNER || ( (kID == g_kWearer) && g_iRunaway ) ){ + string sPrompt; + list lButtons = []; + + if (kID == g_kWearer){ + sPrompt += "[Runaway]\n\nAre you sure you want to runaway from all owners?\n\n* This action will reset your owners list, trusted list, and your blocked avatars list."; + lButtons += ["Yes", "No"]; + } if(iAuth == CMD_OWNER){ sPrompt+="\n\nAs the owner you have the ability to disable or enable runaway."; - if(g_iRunaway)lButtons+=["Disable"]; + if(g_iRunaway)lButtons += ["Disable"]; else lButtons += ["Enable"]; - } else if(kID == g_kWearer){ - if(g_iRunaway){ - sPrompt += "\n\nAs the wearer, you can choose to disable your ability to runaway, this action cannot be reversed by you"; - lButtons += ["Disable"]; - } + } + else if (kID == g_kWearer) { + sPrompt += "\n\nAs the wearer, you can choose to disable your ability to runaway, this action cannot be reversed by you"; + lButtons += ["Disable"]; } Dialog(kID, sPrompt, lButtons, [], 0, iAuth, "RunawayMenu"); } else { llMessageLinked(LINK_SET,NOTIFY,"0%NOACCESS% to runaway, or the runaway settings menu", kID); - //llMessageLinked(LINK_SET,iAuth,"menu Access", kID); } } @@ -225,7 +255,6 @@ WearerConfirmListUpdate(key kID, string sReason) } integer g_iAllowWearerSetTrusted=FALSE; integer g_iGrantedConsent=FALSE; -integer g_iRunawayMode = -1; UpdateLists(key kID, key kIssuer){ //llOwnerSay(llDumpList2String([kID, kIssuer, g_kMenuUser, g_iMode, g_iGrantedConsent], ", ")); integer iMode = g_iMode; @@ -328,8 +357,7 @@ UserCommand(integer iAuth, string sCmd, key kID){ llLoadURL(kID, "Want to open our website for further help?", "https://opencollar.cc"); } } - if((llToLower(sCmd) == "menu runaway" || llToLower(sCmd) == "runaway") && g_iRunawayMode!=2){ - g_iRunawayMode=0; + if((llToLower(sCmd) == "menu runaway") || (llToLower(sCmd) == "runaway")){ RunawayMenu(kID,iAuth); } @@ -388,21 +416,8 @@ UserCommand(integer iAuth, string sCmd, key kID){ } } if (iAuth CMD_EVERYONE) return; - if (iAuth == CMD_OWNER && sCmd == "runaway") { - // trigger runaway sequence if approval was given - if(g_iRunawayMode == 2){ - g_iRunawayMode=-1; - llMessageLinked(LINK_SET, NOTIFY_OWNERS, "Runaway completed on %WEARERNAME%'s collar", kID); - llMessageLinked(LINK_SET, LM_SETTING_DELETE, "auth_owner","origin"); - llMessageLinked(LINK_SET, LM_SETTING_DELETE, "auth_trust","origin"); - llMessageLinked(LINK_SET, LM_SETTING_DELETE, "auth_block","origin"); - llMessageLinked(LINK_SET, NOTIFY, "0Runaway complete", g_kWearer); - return; - } - - } - if(sCmd == "print auth"){ + if(sCmd == "print auth"){ if(iAuth == CMD_OWNER || iAuth == CMD_TRUSTED || iAuth == CMD_WEARER) PrintAccess(kID); else @@ -429,7 +444,6 @@ integer SENSORDIALOG = -9003; integer SAY = 1004; integer g_iInterfaceChannel; - integer g_iStartup=TRUE; default { @@ -455,6 +469,7 @@ state active { on_rez(integer iNum){ llMessageLinked(LINK_SET, ALIVE, llGetScriptName(),""); + g_iStartup=TRUE; } changed(integer change){ if (change & CHANGED_OWNER) llResetScript(); @@ -488,7 +503,6 @@ state active } } - run_time_permissions(integer iPerm) { if (iPerm & PERMISSION_ATTACH) { llOwnerSay("@detach=yes"); @@ -497,66 +511,90 @@ state active } listen(integer c,string n,key i,string m){ - if(c == g_iInterfaceChannel && llGetOwnerKey(i)==g_kWearer){ - //do nothing if wearer isnt owner of the object - if (llGetOwnerKey(i) != g_kWearer) return; - //play ping pong with the Sub AO - if (m == "OpenCollar?") llRegionSayTo(g_kWearer, g_iInterfaceChannel, "OpenCollar=Yes"); - else if (m == "OpenCollar=Yes") { - llOwnerSay("\n\nATTENTION: You are attempting to wear more than one OpenCollar core. This causes errors with other compatible accessories and your RLV relay. For a smooth experience, and to avoid wearing unnecessary script duplicates, please consider to take off \""+n+"\" manually if it doesn't detach automatically.\n"); - llRegionSayTo(i,g_iInterfaceChannel,"There can be only one!"); - } else if (m == "There can be only one!" ) { - llOwnerSay("/me has been detached."); - llRequestPermissions(g_kWearer,PERMISSION_ATTACH); + if(c == g_iInterfaceChannel) { + if (llGetOwnerKey(i)==g_kWearer){ + //play ping pong with the Sub AO only if object is owned by wearer + if (m == "OpenCollar?") llRegionSayTo(g_kWearer, g_iInterfaceChannel, "OpenCollar=Yes"); + else if (m == "OpenCollar=Yes") { + llOwnerSay("\n\nATTENTION: You are attempting to wear more than one OpenCollar core. This causes errors with other compatible accessories and your RLV relay. For a smooth experience, and to avoid wearing unnecessary script duplicates, please consider to take off \""+n+"\" manually if it doesn't detach automatically.\n"); + llRegionSayTo(i,g_iInterfaceChannel,"There can be only one!"); + } else if (m == "There can be only one!" ) { + llOwnerSay("/me has been detached."); + llRequestPermissions(g_kWearer,PERMISSION_ATTACH); + } } - else if(llToLower(llGetSubString(m,0,5))=="menuto") { + if(llToLower(llGetSubString(m,0,5))=="menuto") { m=llStringTrim(llGetSubString(m,6,-1),STRING_TRIM); - if(llGetAgentSize((key)m)) llMessageLinked(LINK_SET,0,"menu",m); + if(llGetAgentSize((key)m)!=ZERO_VECTOR && llGetOwnerKey(i)==(key)m) llMessageLinked(LINK_SET,0,"menu",m); return; } + else { + key kAuthKey=llGetOwnerKey(i); + integer iAuth=CalcAuth(kAuthKey); + if(llGetSubString(m,0,6)=="authas:"){ //messages prefixed authas:(key)=(cmd) will use the auth level of key if LOWER than object owner auth. + kAuthKey=llGetSubString(m,7,42); + m=llGetSubString(m,44,-1); + if(llGetAgentSize(kAuthKey)){ + integer iAKAuth=CalcAuth(kAuthKey); + if(iAKAuth>iAuth) iAuth=iAKAuth; + } + else return; + } + if(llToLower(llGetSubString(m,0,8))=="checkauth" && CalcAuth(kAuthKey)>=CMD_OWNER && CalcAuth(kAuthKey)<=CMD_EVERYONE) { + integer iReplyChan=(integer)llGetSubString(m,10,-1); + if(iReplyChan) llRegionSayTo(i,iReplyChan,"authreply|"+(string)g_kWearer+"|"+(string)CalcAuth(kAuthKey)+"|"+(string)kAuthKey); + } else if (!llSubStringIndex(m,(string)g_kWearer + ":")){ + m = llGetSubString(m, 37, -1); + if(llGetAgentSize(kAuthKey)) llMessageLinked(LINK_SET, iAuth , m , llGetOwnerKey(i)); + } + } + return; } - - - - if(llToLower(llGetSubString(m,0,llStringLength(g_sPrefix)-1))==llToLower(g_sPrefix)){ - string CMD=llGetSubString(m,llStringLength(g_sPrefix),-1); - if(llGetSubString(CMD,0,0)==" ")CMD=llDumpList2String(llParseString2List(CMD,[" "],[]), " "); - llMessageLinked(LINK_SET, CMD_ZERO, CMD, llGetOwnerKey(i)); - } else if(llGetSubString(m,0,0) == "*"){ - string CMD = llGetSubString(m,1,-1); - if(llGetSubString(CMD,0,0)==" ")CMD=llDumpList2String(llParseString2List(CMD,[" "],[])," "); - llMessageLinked(LINK_SET, CMD_ZERO, CMD, llGetOwnerKey(i)); - } else { + string CMD; + if(llSubStringIndex(llToLower(m),llToLower(g_sPrefix))==0) { + CMD=llGetSubString(m,llStringLength(g_sPrefix),-1); + } else if(llGetSubString(m,0,0) == "*" || (llGetSubString(m,0,0)=="#" && i!=g_kWearer)) { + CMD = llGetSubString(m,1,-1); + } + CMD=llStringTrim(CMD,STRING_TRIM); + if(i==g_kWearer) { list lTmp = llParseString2List(m,[" ","(",")"],[]); string sDump = llToLower(llDumpList2String(lTmp, "")); - - if(sDump == llToLower(g_sSafeword) && !g_iSafewordDisable && i == g_kWearer){ + if(sDump == llToLower(g_sSafeword) && !g_iSafewordDisable) { llMessageLinked(LINK_SET, CMD_SAFEWORD, "",""); + llLinksetDataDelete("auth_WearerLockout"); SW(); + return; + } + else if(llLinksetDataRead("auth_WearerLockout")!="" && CMD!="runaway" && CMD!="") + { + llMessageLinked(LINK_SET,AUTH_WEARERLOCKOUT,"",""); + return; } } + + if(CMD!="" && CMD!="initialize" && CMD!="runaway_confirmed") { + llMessageLinked(LINK_SET, CalcAuth(llGetOwnerKey(i)),llStringTrim(CMD,STRING_TRIM_HEAD),llGetOwnerKey(i)); + } + } link_message(integer iSender, integer iNum, string sStr, key kID){ - - //if(iNum>=CMD_OWNER && iNum <= CMD_NOACCESS) llOwnerSay(llDumpList2String([iSender, iNum, sStr, kID], " ^ ")); if(iNum == CMD_ZERO){ if(sStr == "initialize")return; integer iAuth = CalcAuth(kID); //llOwnerSay( "{API} Calculate auth for "+(string)kID+"="+(string)iAuth+";"+sStr); - llMessageLinked(LINK_SET, iAuth, sStr, kID); + if(llLinksetDataRead("auth_WearerLockout")!="" && kID==g_kWearer && iAuth==CMD_NOACCESS ) { + llMessageLinked(LINK_SET,AUTH_WEARERLOCKOUT,"",""); + return; + } else llMessageLinked(LINK_SET, iAuth, sStr, kID); } else if(iNum == AUTH_REQUEST){ integer iAuth = CalcAuth(kID); //llOwnerSay("{API} Calculate auth for "+(string)kID+"="+(string)iAuth+";"+sStr); llMessageLinked(LINK_SET, AUTH_REPLY, "AuthReply|"+(string)kID+"|"+(string)iAuth,sStr); } else if(iNum >= CMD_OWNER && iNum <= CMD_NOACCESS) UserCommand(iNum, sStr, kID); - - else if (iNum == DIALOG_TIMEOUT) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex +3); //remove stride from g_lMenuIDs - } else if(iNum == LM_SETTING_RESPONSE){ list lPar = llParseString2List(sStr, ["_","="],[]); string sToken = llList2String(lPar,0); @@ -671,10 +709,9 @@ state active } else if(iNum == STARTUP && sStr=="ALL"){ llMessageLinked(LINK_SET, LM_SETTING_REQUEST, "ALL",""); } else if(iNum == DIALOG_RESPONSE){ - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - if(iMenuIndex!=-1){ - string sMenu = llList2String(g_lMenuIDs, iMenuIndex+1); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex-1, iMenuIndex-2+g_iMenuStride); + integer iPos = llSubStringIndex(kID, "~"+llGetScriptName()); + if(iPos>0){ + string sMenu = llGetSubString(kID, 0, iPos-1); list lMenuParams = llParseString2List(sStr, ["|"],[]); key kAv = llList2Key(lMenuParams,0); string sMsg = llList2String(lMenuParams,1); @@ -734,24 +771,23 @@ state active } else if(sMsg == "Disable"){ g_kDenyRunawayRequester=kAv; Dialog(g_kWearer, "\nsecondlife:///app/agent/"+(string)kAv+"/about wants to disable your ability to 'Runaway'. This can only be reversed by an owner. \n\nYou may accept or deny this action.", [], ["Accept", "Deny"], 0, CMD_WEARER, "confirmdenyrunaway"); - // g_iRunaway=FALSE; - // llMessageLinked(LINK_SET, LM_SETTING_SAVE, "AUTH_runaway=0", "origin"); - // llMessageLinked(LINK_SET, TIMEOUT_REGISTER, "5", "spring_access:"+(string)kAv); } else if(sMsg == "No"){ - // return - g_iRunawayMode=-1; llMessageLinked(LINK_SET, TIMEOUT_REGISTER, "2", "spring_access:"+(string)kAv); return; } else if(sMsg == "Yes"){ - // trigger runaway if((kAv == g_kWearer || iAuth == CMD_OWNER ) && g_iRunaway){ - g_iRunawayMode=2; + // trigger runaway sequence if approval was given llMessageLinked(LINK_SET, NOTIFY_OWNERS, "%WEARERNAME% has runaway.", ""); - llMessageLinked(LINK_SET, CMD_OWNER, "runaway", g_kWearer); + llMessageLinked(LINK_SET, CMD_OWNER, "runaway_confirmed", g_kWearer); llMessageLinked(LINK_SET, CMD_SAFEWORD, "safeword", ""); - llMessageLinked(LINK_SET, CMD_OWNER, "clear", g_kWearer); - + llSleep(0.5); // Wait for notifications before clearing owners + llLinksetDataDelete("auth_WearerLockout"); llMessageLinked(LINK_SET, TIMEOUT_REGISTER, "5", "spring_access:"+(string)kAv); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "auth_owner","origin"); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "auth_trust","origin"); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "auth_block","origin"); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "auth_group","origin"); + llMessageLinked(LINK_SET, NOTIFY, "0Runaway complete", g_kWearer); } } } else if(sMenu=="confirmdenyrunaway") { @@ -760,7 +796,7 @@ state active llMessageLinked(LINK_SET, LM_SETTING_SAVE, "AUTH_runaway=0", "origin"); llMessageLinked(LINK_SET, TIMEOUT_REGISTER, "2", "spring_access:"+(string)g_kDenyRunawayRequester); } else if (sMsg=="Deny"){ - llMessageLinked(LINK_SET, NOTIFY, "Wearer has DENIED switching off runaway.",g_kDenyRunawayRequester); + llMessageLinked(LINK_SET, NOTIFY, "0Wearer has DENIED switching off runaway.",g_kDenyRunawayRequester); llMessageLinked(LINK_SET, TIMEOUT_REGISTER, "2", "spring_access:"+(string)g_kDenyRunawayRequester); } } diff --git a/src/collar/oc_bell.lsl b/src/collar/oc_bell.lsl index b5a80dbd8..de94e4a44 100644 --- a/src/collar/oc_bell.lsl +++ b/src/collar/oc_bell.lsl @@ -1,7 +1,8 @@ // This file is part of OpenCollar. -// Copyright (c) 2009 - 2016 Cleo Collins, Nandana Singh, Satomi Ahn, +// Copyright (c) 2009 - 2024 Cleo Collins, Nandana Singh, Satomi Ahn, // Joy Stipe, Wendy Starfall, Medea Destiny, littlemousy, -// Romka Swallowtail, Garvin Twine et al. +// Romka Swallowtail, Garvin Twine et al. +// Last updated: Trix (TrixAeo) 2024 - Uploaded new sounds. // Licensed under the GPLv2. See LICENSE for full details. @@ -9,7 +10,17 @@ //show/hide for elements named: Bell //2009-01-30 Cleo Collins - 1. draft -string g_sScriptVersion = "8.1"; +/* Other contributors + +Trix7125 + *Feb 2024 - New bell sounds with normalized volumes +AdmiralTails + *Aug 2023 - Ringing of bell dependent on On status, not Hidden status, as menu states. + +*/ + + +string g_sScriptVersion = "8.3"; integer LINK_CMD_DEBUG=1999; DebugOutput(key kID, list ITEMS){ integer i=0; @@ -48,9 +59,21 @@ integer g_iBellShow=FALSE; // is the bell visible string g_sBellShow="SHOW"; //menu text of bell visible string g_sBellHide="HIDE"; //menu text of bell hidden -//list g_listBellSounds=["7b04c2ee-90d9-99b8-fd70-8e212a72f90d","b442e334-cb8a-c30e-bcd0-5923f2cb175a","1acaf624-1d91-a5d5-5eca-17a44945f8b0","5ef4a0e7-345f-d9d1-ae7f-70b316e73742","da186b64-db0a-bba6-8852-75805cb10008","d4110266-f923-596f-5885-aaf4d73ec8c0","5c6dd6bc-1675-c57e-0847-5144e5611ef9","1dc1e689-3fd8-13c5-b57f-3fedd06b827a"]; // list with legacy bell sounds +// List of current sounds +// bell_brass_01.wav 9a1f0d2e-0f32-21f7-7d83-1aeb428ace4a +// bell_brass_02.wav b6985bfd-72c0-5e19-5d83-88f6c9bbe151 +// bell_cow_01.wav 5837c0d4-cdb5-21f5-8743-5801614383a8 +// bell_cow_02.wav 86c7c7ec-1862-5328-f908-00d341e0a5d2 +// bell_cow_03.wav effb7813-18a3-7237-dcf0-873713e4a545 +// bell_plastic_01.wav f75aaed4-2415-d5a0-bd94-4bb32432b42f +// bell_silver_01.wav f8be56c4-28c8-3095-4b61-d425467eb2cb +// bell_silver_02.wav e0b061c0-9edd-6a49-cdd6-a197ab439391 +// bell_silver_03.wav 378e5546-955d-b04f-8994-c7b0763b8f68 + + list g_listBellSounds=["9a1f0d2e-0f32-21f7-7d83-1aeb428ace4a","b6985bfd-72c0-5e19-5d83-88f6c9bbe151","5837c0d4-cdb5-21f5-8743-5801614383a8", +"86c7c7ec-1862-5328-f908-00d341e0a5d2", "effb7813-18a3-7237-dcf0-873713e4a545", "f75aaed4-2415-d5a0-bd94-4bb32432b42f", "f8be56c4-28c8-3095-4b61-d425467eb2cb", + "e0b061c0-9edd-6a49-cdd6-a197ab439391", "378e5546-955d-b04f-8994-c7b0763b8f68"]; -list g_listBellSounds=["ae3a836f-4d69-2b74-1d52-9c78a9106206","503d2360-99f8-7a4a-8b89-43c5122927bd","a3ff9ca6-8289-0007-5b6b-d4c993580a6b","843adc44-1189-2d67-6f3a-72a80b3a9ed4","4c84b9b7-b363-b501-c019-8eef5fb4d3c2","3b95831e-8da5-597f-3b4d-713a03945cb6","285b317c-23d1-de51-84bc-938eb3df9e46","074b9b37-f6a3-a0a3-f40e-14bc57502435"]; // list with 4.0 bell sounds key g_kCurrentBellSound; // curent bell sound key integer g_iCurrentBellSound; // curent bell sound sumber integer g_iBellSoundCount; // number of avail bell sounds @@ -429,7 +452,6 @@ state active control( key kID, integer nHeld, integer nChange ) { if (!g_iBellOn) return; - if(!g_iBellShow)return; //the user is pressing a movement key if ((nChange & (CONTROL_LEFT|CONTROL_RIGHT|CONTROL_DOWN|CONTROL_UP|CONTROL_FWD|CONTROL_BACK)) && llGetTime()>g_fNextRing) llPlaySound(g_kCurrentBellSound,g_fVolume); diff --git a/src/collar/oc_bookmarks.lsl b/src/collar/oc_bookmarks.lsl index 7bb56a4db..777008acb 100644 --- a/src/collar/oc_bookmarks.lsl +++ b/src/collar/oc_bookmarks.lsl @@ -7,10 +7,10 @@ /* Medea Destiny - Dec 2021 - Provided clickable SLURL feedback to command issuer - + Aug 2024 - Fix for above, escaping sim name with spaces, issue #1026 */ -string g_sScriptVersion = "8.2"; +string g_sScriptVersion = "8.3"; integer LINK_CMD_DEBUG=1999; DebugOutput(key kID, list ITEMS){ integer i=0; @@ -354,7 +354,7 @@ TeleportTo(string sStr,key kIssuer) { //take a string in region (x,y,z) format, llMapDestination(sRegion, g_vLocalPos, ZERO_VECTOR); else //We've got RLV, let's use it g_kRequestHandle = llRequestSimulatorData(sRegion, DATA_SIM_POS); - llRegionSayTo(kIssuer,0,"Sending "+llGetDisplayName(g_kWearer)+" to http://maps.secondlife.com/secondlife/"+sRegion+"/"+(string)((integer)g_vLocalPos.x) + "/"+(string)((integer)g_vLocalPos.y)+"/"+(string)((integer)g_vLocalPos.z)+"."); + llRegionSayTo(kIssuer,0,"Sending "+llGetDisplayName(g_kWearer)+" to http://maps.secondlife.com/secondlife/"+llEscapeURL(sRegion)+"/"+(string)((integer)g_vLocalPos.x) + "/"+(string)((integer)g_vLocalPos.y)+"/"+(string)((integer)g_vLocalPos.z)+"."); } diff --git a/src/collar/oc_core.lsl b/src/collar/oc_core.lsl index 2a93a518f..a1800fab9 100644 --- a/src/collar/oc_core.lsl +++ b/src/collar/oc_core.lsl @@ -33,12 +33,23 @@ Medea (Medea Destiny) And kAv == g_kWearer instead of iAuth == CMD_WEARER in meu dialog responses for: + / - trusted / blacklist when wearer is permitted, displaying access list, print settings Oct 2022 - Fix for full version>beta version checking. Added menu text to clarify versioning for beta users. - + Oct 2023 - Refactor of safeword function in usercommand. 'Safeword off' now no longer sets safeword to 'off' + before disabling, resulting in confusing "Safeword is now set to 'off'" message. Instead safeword + off is clearly notified. Wearer can now set their own safeword, but only owners can disable it still. + See issue # 986. Attempting to access safeword without permission now gives no access response. + - Provide no access notification for device name, and allow non-owner wearer to name. Notify wearer + as well when another person changes device name. See issue # 987 + Jul 2024 - Further work on above safeword stuff, see PR #999 + - added delay after name change to ensure report is correct and added clarification text here + and in device name. Issue #1053 + Stormed Darkshade (StormedStormy) March 2022 - Added a button for reboot to help/about menu. Yosty7B3 - Nov 2022 - Removed Setor() and bool() functions for streamlining. + Nov 2022 - Removed Setor() and bool() functions for streamlining. + Aug 2023 - Combine all menu functions into the Dialog function to save memory. + Licensed under the GPLv2. See LICENSE for full details. https://github.com/OpenCollarTeam/OpenCollar */ @@ -47,7 +58,7 @@ integer NOTIFY_OWNERS=1003; //string g_sParentMenu = ""; string g_sSubMenu = "Main"; -string COLLAR_VERSION = "8.2.3000"; // Provide enough room +string COLLAR_VERSION = "8.3.0000"; // Provide enough room // LEGEND: Major.Minor.Build RC Beta Alpha integer UPDATE_AVAILABLE=FALSE; string NEW_VERSION = ""; @@ -61,6 +72,7 @@ integer g_iVerbosityLevel=1; integer g_iNotifyInfo=FALSE; string g_sSafeword="RED"; +integer g_iSafewordDisable=0; //MESSAGE MAP //integer CMD_ZERO = 0; integer CMD_OWNER = 500; @@ -114,99 +126,92 @@ integer g_iListenPublic=TRUE; key g_kWeldBy; list g_lMainMenu=["Apps", "Access", "Settings", "Help/About"]; -Dialog(key kID, string sPrompt, list lChoices, list lUtilityButtons, integer iPage, integer iAuth, string sName) { - key kMenuID = llGenerateKey(); - llMessageLinked(LINK_SET, DIALOG, (string)kID + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lChoices, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, kMenuID); - - integer iIndex = llListFindList(g_lMenuIDs, [kID]); - if (~iIndex) g_lMenuIDs = llListReplaceList(g_lMenuIDs, [kID, kMenuID, sName], iIndex, iIndex + g_iMenuStride - 1); - else g_lMenuIDs += [kID, kMenuID, sName]; +Dialog(key kID, string sPrompt, list lButtons, list lUtilityButtons, integer iPage, integer iAuth, string sName) { + if(sName == "Menu~Settings"){ + sPrompt = "OpenCollar\n\n[Settings]\n\n'Print' lists settings in chat, 'Load' reloads setting from notecard. Use 'Fix Menus' if menus are missing. 'EDITOR' allows manual editing of settings. 'Limit Range' to ignore clicks from distant users. 'Listen 0' controls whether the collar will hear commands in local chat."; + lButtons = [Checkbox(g_iListenPublic,"Listen 0"),Checkbox(g_iLimitRange, "Limit Range"),"Print", "Load", "Fix Menus"]; + if (llGetInventoryType("oc_resizer") == INVENTORY_SCRIPT) lButtons += ["Resize"]; + else lButtons += ["-"]; + lButtons += [Checkbox(g_iHide, "Hide"), "EDITOR", Checkbox(g_iAllowHide, "AllowHiding"), "Addon.."]; + + lUtilityButtons = [UPMENU]; + + }else if(sName == "Menu~SAddons"){ + sPrompt = "OpenCollar\n\n[Addon Settings\n\nWearerAddons - Allow/Disallow use of wearer owned addons\nAddonLimited - Limit whether wearer owned addons can modify the owners list or weld state (default enabled)"; + lButtons = [Checkbox(g_iWearerAddons, "WearerAddons"), Checkbox(g_iWearerAddonLimited, "AddonLimited"), Checkbox(g_iAddons, "Addons")]; + + lUtilityButtons = [UPMENU]; + + }else if(sName == "Menu~Apps"){ + sPrompt = "\n[Apps]\nYou have "+(string)llGetListLength(g_lApps)+" apps installed"; + lButtons = g_lApps; + + lUtilityButtons = [UPMENU]; + + }else if(sName == "Menu~Main"){ + sPrompt = "\nOpenCollar "+COLLAR_VERSION; + lButtons = [Checkbox(g_iLocked, "Lock")]; + + if(!g_iWelded)lButtons+=g_lMainMenu; + else lButtons=g_lMainMenu; + + if(UPDATE_AVAILABLE ) sPrompt += "\n\nUPDATE AVAILABLE: Your version is: "+COLLAR_VERSION+", The current release version is: "+NEW_VERSION; + if(g_iAmNewer)sPrompt+="\n\nYour collar version is newer than the public release. This may happen if you are using a beta or pre-release copy.\nNote: Pre-Releases may have bugs. Ensure you report any bugs to [https://github.com/OpenCollarTeam/OpenCollar Github]"; + if(g_iIsBeta)sPrompt+="\n(The last 3 digits indicate a pre-release version, which is superseded by 000 for a release version)."; + + if(g_iWelded) sPrompt+="\n\n* The Collar is Welded by secondlife:///app/agent/"+(string)g_kWeldBy+"/about *"; + if(iAuth==CMD_OWNER && g_iLocked && !g_iWelded)lButtons+=["Weld"]; + + }else if(sName == "Menu~Auth"){ + if(g_iCaptured){ + llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to the access settings while capture is active!", kID); + llMessageLinked(LINK_SET, 0, "menu", kID); + return; + } + sPrompt = "\nOpenCollar Access Controls\n+/- buttons to control access lists.\nGroup allows access to current group, Public allows access to all. Wearer trust allows an owned wearer to add/remove from trusted list.\nRunaway removes all owners, access list prints out who has access."; + lButtons = ["+ Owner", "+ Trust", "+ Block", "- Owner", "- Trust", "- Block", Checkbox(g_kGroup!="", "Group"), Checkbox(g_iPublic, "Public")]; + + lButtons += [Checkbox(g_iAllowWearerSetTrusted, "Wearer Trust"), "Runaway", "Access List"]; + + lUtilityButtons = [UPMENU]; + + }else if(sName == "Menu~Help"){ + string EXTRA_VER_TXT; + if(llGetSubString(COLLAR_VERSION,-1,-1)!="0") EXTRA_VER_TXT = " (ALPHA "+llGetSubString(COLLAR_VERSION,-1,-1)+") "; + if(llGetSubString(COLLAR_VERSION,-2,-2)!="0") EXTRA_VER_TXT += " (BETA "+llGetSubString(COLLAR_VERSION,-2,-2)+") "; + if(llGetSubString(COLLAR_VERSION,-3,-3)!="0") EXTRA_VER_TXT += " (RC " + llGetSubString(COLLAR_VERSION,-3,-3)+") "; + + sPrompt = "\nOpenCollar "+COLLAR_VERSION+" "+EXTRA_VER_TXT+"\nVersion: "+llList2String(["","(Newer than release)"],g_iAmNewer)+" "+llList2String(["(Most Current Version)","(Update Available)"],UPDATE_AVAILABLE); + sPrompt += "\n\nDocumentation https://opencollar.cc"; + sPrompt += "\nPrefix: "+g_sPrefix+"\nChannel: "+(string)g_iChannel; + sPrompt += "\nSafeword: "+g_sSafeword; + if(g_iNotifyInfo){ + g_iNotifyInfo=FALSE; + llMessageLinked(LINK_SET, NOTIFY, sPrompt, kID); + return; + } + lButtons = ["Update", "Support", "License", "Reboot"]; + + lUtilityButtons = [UPMENU]; + } + + llMessageLinked(LINK_SET, DIALOG, (string)kID + "|" + sPrompt + "|"+(string)iPage+"|" + llDumpList2String(lButtons, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, sName+"~"+llGetScriptName()); } integer g_iHide=FALSE; integer g_iAllowHide=TRUE; -Settings(key kID, integer iAuth){ - string sPrompt = "OpenCollar\n\n[Settings]\n\n'Print' lists settings in chat, 'Load' reloads setting from notecard. Use 'Fix Menus' if menus are missing. 'EDITOR' allows manual editing of settings. 'Limit Range' to ignore clicks from distant users. 'Listen 0' controls whether the collar will hear commands in local chat."; - list lButtons = [Checkbox(g_iListenPublic,"Listen 0"),Checkbox(g_iLimitRange, "Limit Range"),"Print", "Load", "Fix Menus"]; - if (llGetInventoryType("oc_resizer") == INVENTORY_SCRIPT) lButtons += ["Resize"]; - else lButtons += ["-"]; - lButtons += [Checkbox(g_iHide, "Hide"), "EDITOR", Checkbox(g_iAllowHide, "AllowHiding"), "Addon.."]; - Dialog(kID, sPrompt, lButtons, [UPMENU],0,iAuth, "Menu~Settings"); -} - -AddonSettings(key kID, integer iAuth) -{ - string sPrompt = "OpenCollar\n\n[Addon Settings\n\nWearerAddons - Allow/Disallow use of wearer owned addons\nAddonLimited - Limit whether wearer owned addons can modify the owners list or weld state (default enabled)"; - list lButtons = [Checkbox(g_iWearerAddons, "WearerAddons"), Checkbox(g_iWearerAddonLimited, "AddonLimited"), Checkbox(g_iAddons, "Addons")]; - Dialog(kID, sPrompt, lButtons, [UPMENU], 0, iAuth, "Menu~SAddons"); -} integer g_iWelded=FALSE; integer g_iWearerAddons=TRUE; // The original idea in #356, was to make this as a app, but i fail to see why we must use an extra app just to create the weld, the extra app or possibly an addon could be made to unweld should the wearer desire it. integer g_iAddons=TRUE; list g_lApps; -AppsMenu(key kID, integer iAuth){ - string sPrompt = "\n[Apps]\nYou have "+(string)llGetListLength(g_lApps)+" apps installed"; - Dialog(kID, sPrompt, g_lApps, [UPMENU],0,iAuth, "Menu~Apps"); -} - -Menu(key kID, integer iAuth) { - string sPrompt = "\nOpenCollar "+COLLAR_VERSION; - list lButtons = [Checkbox(g_iLocked, "Lock")]; - - if(!g_iWelded)lButtons+=g_lMainMenu; - else lButtons=g_lMainMenu; - - if(UPDATE_AVAILABLE ) sPrompt += "\n\nUPDATE AVAILABLE: Your version is: "+COLLAR_VERSION+", The current release version is: "+NEW_VERSION; - if(g_iAmNewer)sPrompt+="\n\nYour collar version is newer than the public release. This may happen if you are using a beta or pre-release copy.\nNote: Pre-Releases may have bugs. Ensure you report any bugs to [https://github.com/OpenCollarTeam/OpenCollar Github]"; - if(g_iIsBeta)sPrompt+="\n(The last 3 digits indicate a pre-release version, which is superseded by 000 for a release version)."; - - if(g_iWelded)sPrompt+="\n\n* The Collar is Welded by secondlife:///app/agent/"+(string)g_kWeldBy+"/about *"; - if(iAuth==CMD_OWNER && g_iLocked && !g_iWelded)lButtons+=["Weld"]; - - - list lUtility; - - Dialog(kID, sPrompt, lButtons, lUtility, 0, iAuth, "Menu~Main"); -} key g_kGroup = ""; integer g_iLimitRange=TRUE; integer g_iPublic=FALSE; integer g_iCaptured = FALSE; integer g_iAllowWearerSetTrusted=FALSE; -AccessMenu(key kID, integer iAuth){ - if(g_iCaptured){ - llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to the access settings while capture is active!", kID); - llMessageLinked(LINK_SET, 0, "menu", kID); - return; - } - string sPrompt = "\nOpenCollar Access Controls\n+/- buttons to control access lists.\nGroup allows access to current group, Public allows access to all. Wearer trust allows an owned wearer to add/remove from trusted list.\nRunaway removes all owners, access list prints out who has access."; - list lButtons = ["+ Owner", "+ Trust", "+ Block", "- Owner", "- Trust", "- Block", Checkbox(g_kGroup!="", "Group"), Checkbox(g_iPublic, "Public")]; - - lButtons += [Checkbox(g_iAllowWearerSetTrusted, "Wearer Trust"), "Runaway", "Access List"]; - Dialog(kID, sPrompt, lButtons, [UPMENU], 0, iAuth, "Menu~Auth"); -} -HelpMenu(key kID, integer iAuth){ - string EXTRA_VER_TXT; - if(llGetSubString(COLLAR_VERSION,-1,-1)!="0") EXTRA_VER_TXT = " (ALPHA "+llGetSubString(COLLAR_VERSION,-1,-1)+") "; - if(llGetSubString(COLLAR_VERSION,-2,-2)!="0") EXTRA_VER_TXT += " (BETA "+llGetSubString(COLLAR_VERSION,-2,-2)+") "; - if(llGetSubString(COLLAR_VERSION,-3,-3)!="0") EXTRA_VER_TXT += " (RC " + llGetSubString(COLLAR_VERSION,-3,-3)+") "; - - string sPrompt = "\nOpenCollar "+COLLAR_VERSION+" "+EXTRA_VER_TXT+"\nVersion: "+llList2String(["","(Newer than release)"],g_iAmNewer)+" "+llList2String(["(Most Current Version)","(Update Available)"],UPDATE_AVAILABLE); - sPrompt += "\n\nDocumentation https://opencollar.cc"; - sPrompt += "\nPrefix: "+g_sPrefix+"\nChannel: "+(string)g_iChannel; - sPrompt += "\nSafeword: "+g_sSafeword; - if(g_iNotifyInfo){ - g_iNotifyInfo=FALSE; - llMessageLinked(LINK_SET, NOTIFY, sPrompt, kID); - return; - } - list lButtons = ["Update", "Support", "License", "Reboot"]; - Dialog(kID, sPrompt, lButtons, [UPMENU], 0, iAuth, "Menu~Help"); -} - -list g_lCheckboxes=["▢", "▣"]; +list g_lCheckboxes=["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, (iValue>0))+" "+sLabel; } @@ -223,7 +228,7 @@ UserCommand(integer iNum, string sStr, key kID) { llMessageLinked(LINK_SET, LM_SETTING_DELETE, "auth_block","origin"); return; } - if (sStr==g_sSubMenu || sStr == "menu "+g_sSubMenu || sStr == "menu") Menu(kID, iNum); + if (sStr==g_sSubMenu || sStr == "menu "+g_sSubMenu || sStr == "menu") Dialog(kID,"",[],[],0,iNum,"Menu~Main"); //else if (iNum!=CMD_OWNER && iNum!=CMD_TRUSTED && kID!=g_kWearer) RelayNotify(kID,"Access denied!",0); else { //integer iWSuccess = 0; @@ -255,36 +260,39 @@ UserCommand(integer iNum, string sStr, key kID) { g_iWaitUpdate = TRUE; llSetTimerEvent(5); } else llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to update the collar", kID); - } else if(sChangetype == "safeword"){ - if(sChangevalue!=""){ - if(iNum == CMD_OWNER){ - llMessageLinked(LINK_SET, LM_SETTING_SAVE, "global_safeword="+sChangevalue, ""); - llMessageLinked(LINK_SET,NOTIFY,"1Safeword is now set to '"+sChangevalue,kID); - - if(sChangevalue == "RED"){ - llMessageLinked(LINK_SET, LM_SETTING_DELETE, "global_safeword",""); - } - - if(llToLower(sChangevalue) == "off"){ - llMessageLinked(LINK_SET, LM_SETTING_SAVE, "global_safeworddisable=1", ""); - } else { - llMessageLinked(LINK_SET, LM_SETTING_DELETE, "global_safeworddisable",""); - } + } else if(sChangetype == "safeword") { + if(iNum!=CMD_OWNER && kID!=g_kWearer) { + llMessageLinked(LINK_SET,NOTIFY,"0No access to safeword!",kID); + return; + } if(llToLower(sChangevalue) == "off") { + if(iNum==CMD_OWNER) { + llMessageLinked(LINK_SET, LM_SETTING_SAVE, "global_safeworddisable=1", ""); + llMessageLinked(LINK_SET,NOTIFY,"1Safeword Disabled.",kID); + } else llMessageLinked(LINK_SET,NOTIFY,"0Only an owner can disable Safeword!",kID); + return; + } else if(sChangevalue!="") { + if(g_iSafewordDisable==TRUE && iNum!=CMD_OWNER) { + llMessageLinked(LINK_SET,NOTIFY,"0Only Owners can set a safeword when disabled!",kID); + return; } + if(sChangevalue == "RED") llMessageLinked(LINK_SET, LM_SETTING_DELETE, "global_safeword",""); + else llMessageLinked(LINK_SET, LM_SETTING_SAVE, "global_safeword="+sChangevalue,""); + llMessageLinked(LINK_SET,NOTIFY,"1Safeword is now set to '"+sChangevalue+"'.",kID); + llMessageLinked(LINK_SET, LM_SETTING_DELETE, "global_safeworddisable",""); + llMessageLinked(LINK_SET, CMD_OWNER, "safeword-enable",""); } else { - if(iNum == CMD_OWNER || kID == g_kWearer){ - llMessageLinked(LINK_SET, NOTIFY, "0The safeword is current set to: '"+g_sSafeword+"'",kID); - } + if(g_iSafewordDisable) llMessageLinked(LINK_SET, NOTIFY, "0The safeword is currently disabled.",kID); + else llMessageLinked(LINK_SET, NOTIFY, "0The safeword is currently set to: '"+g_sSafeword+"'",kID); } } else if(sChangetype == "menu"){ if(llToLower(sChangevalue) == "access"){ - AccessMenu(kID,iNum); + Dialog(kID,"",[],[],0,iNum,"Menu~Auth"); } else if(llToLower(sChangevalue) == "settings"){ - Settings(kID,iNum); + Dialog(kID,"",[],[],0,iNum,"Menu~Settings"); } else if(llToLower(sChangevalue) == "apps"){ - AppsMenu(kID,iNum); + Dialog(kID,"",[],[],0,iNum, "Menu~Apps"); } else if(llToLower(sChangevalue) == "help/about"){ - HelpMenu(kID,iNum); + Dialog(kID,"",[],[],0,iNum,"Menu~Help"); } } else if(llToLower(sChangetype) == "weld"){ if(iNum==CMD_OWNER){ @@ -300,7 +308,7 @@ UserCommand(integer iNum, string sStr, key kID) { } else if(llToLower(sChangetype) == "info"){ if(iNum >= CMD_OWNER && iNum <= CMD_EVERYONE){ g_iNotifyInfo = TRUE; - HelpMenu(kID,iNum); + Dialog(kID,"",[],[],0,iNum,"Menu~Help"); } else llMessageLinked(LINK_SET,NOTIFY,"0%NOACCESS%",kID); } else if(llToLower(sChangetype) == "touchnotify"){ if(g_iTouchNotify) llMessageLinked(LINK_SET,NOTIFY,"1The wearer will no longer be notified when someone touches their collar", kID); @@ -316,8 +324,13 @@ UserCommand(integer iNum, string sStr, key kID) { return; } llMessageLinked(LINK_SET, LM_SETTING_SAVE, "global_wearername="+sChangevalue, ""); - llMessageLinked(LINK_SET, NOTIFY, "0The wearer's name is now set to %WEARERNAME%", kID); - } else if(llToLower(sChangetype) == "device" && iNum == CMD_OWNER){ + llSleep(0.5); + llMessageLinked(LINK_SET, NOTIFY, "1The wearer's name is now set to %WEARERNAME% (if this is the old name, please type '/1 (prefix) name' to confirm the change went through, we may just have lagged)", kID); + } else if(llToLower(sChangetype) == "device"){ + if(iNum!=CMD_OWNER && kID!=g_kWearer){ + llMessageLinked(LINK_THIS,NOTIFY,"No access to device name.",kID); + return; + } if(llToLower(sChangevalue) == "name"){ sChangevalue = llDumpList2String(llList2List(lParameters,2,-1), " "); if(llGetListLength(lParameters) == 2){ @@ -326,19 +339,19 @@ UserCommand(integer iNum, string sStr, key kID) { return; } llMessageLinked(LINK_SET, LM_SETTING_SAVE, "global_devicename="+sChangevalue,""); - llSleep(0.4); //To ensure the notify happens AFTER the new device name is in place. - llMessageLinked(LINK_SET, NOTIFY, "0The device name is now set to: %DEVICENAME%", kID); + llSleep(0.5); //To ensure the notify happens AFTER the new device name is in place. + llMessageLinked(LINK_SET, NOTIFY, "1The device name is now set to: %DEVICENAME% (if this is the old name, please type '/1 (prefix) device name' to confirm the change went through, we may just have lagged)", kID); } } else if(llToLower(sChangetype) == "allowhide"){ if(iNum == CMD_OWNER){ if(g_iAllowHide)llMessageLinked(LINK_SET, NOTIFY, "0The wearer can no longer hide the collar", kID); else llMessageLinked(LINK_SET,NOTIFY, "0The wearer can hide the collar on their own", kID); g_iAllowHide=1-g_iAllowHide; - if(sChangevalue=="remenu")Settings(kID,iNum); + if(sChangevalue=="remenu")Dialog(kID,"",[],[],0,iNum,"Menu~Settings"); llMessageLinked(LINK_SET, LM_SETTING_SAVE, "global_allowhide="+(string)g_iAllowHide, ""); } else { llMessageLinked(LINK_SET,NOTIFY,"0%NOACCESS% to toggling Allow Hide", kID); - if(sChangevalue == "remenu")Settings(kID,iNum); + if(sChangevalue == "remenu")Dialog(kID,"",[],[],0,iNum,"Menu~Settings"); } } else if(llToLower(sChangetype)=="lock" && !g_iWelded && (iNum == CMD_OWNER || kID == g_kWearer)){ // allow locking @@ -353,10 +366,10 @@ UserCommand(integer iNum, string sStr, key kID) { llMessageLinked(LINK_SET, NOTIFY, "1%WEARERNAME%'s collar has been unlocked", kID); } else { if(sChangevalue!="")return; - if(llToLower(sChangetype) == "access")AccessMenu(kID,iNum); - else if(llToLower(sChangetype) == "settings")Settings(kID,iNum); - else if(llToLower(sChangetype) == "apps")AppsMenu(kID,iNum); - else if(llToLower(sChangetype) == "help/about") HelpMenu(kID,iNum); + if(llToLower(sChangetype) == "access")Dialog(kID,"",[],[],0,iNum,"Menu~Auth"); + else if(llToLower(sChangetype) == "settings")Dialog(kID,"",[],[],0,iNum,"Menu~Settings"); + else if(llToLower(sChangetype) == "apps")Dialog(kID,"",[],[],0,iNum, "Menu~Apps"); + else if(llToLower(sChangetype) == "help/about") Dialog(kID,"",[],[],0,iNum,"Menu~Help"); } } } @@ -369,8 +382,6 @@ integer g_iUpdateAuth; integer g_iWaitUpdate; integer g_iUpdateChan = -7483213; key g_kWearer; -list g_lMenuIDs; -integer g_iMenuStride; integer g_iLocked=FALSE; Compare(string V1, string V2){ V2=llStringTrim(V2,STRING_TRIM); @@ -497,10 +508,9 @@ state active } else if(iNum == DIALOG_RESPONSE){ - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - if(iMenuIndex!=-1){ - string sMenu = llList2String(g_lMenuIDs, iMenuIndex+1); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex-1, iMenuIndex-2+g_iMenuStride); + integer iPos = llSubStringIndex(kID, "~"+llGetScriptName()); + if(iPos>0){ + string sMenu = llGetSubString(kID, 0, iPos-1); list lMenuParams = llParseString2List(sStr, ["|"],[]); key kAv = llList2Key(lMenuParams,0); string sMsg = llList2String(lMenuParams,1); @@ -523,9 +533,6 @@ state active // don't recaculate while developing llMessageLinked(LINK_SET, iAuth,"menu "+ sMsg, kAv); // Recalculate } - - - if(iRespring)Menu(kAv,iAuth); } else if(sMenu == "weld~consent"){ if(sMsg == "No"){ llMessageLinked(LINK_SET, NOTIFY, "1%NOACCESS% to welding the collar.", g_kWelder); @@ -535,10 +542,11 @@ state active llMessageLinked(LINK_SET, LM_SETTING_SAVE, "intern_weld=1", g_kWelder); g_iWelded=TRUE; } + iRespring=FALSE; } else if(sMenu=="Menu~Auth"){ if(sMsg == UPMENU){ iRespring=FALSE; - Menu(kAv,iAuth); + Dialog(kAv,"",[],[],0,iAuth,"Menu~Main"); } else if(llGetSubString(sMsg,0,0) == "+"){ if(iAuth == CMD_OWNER || (kAv == g_kWearer && (sMsg=="+ Trust"||sMsg=="+ Block") && g_iAllowWearerSetTrusted==TRUE) ){ iRespring=FALSE; @@ -597,11 +605,10 @@ state active } else llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to allowing wearer control of trusted/blocklist",kAv); } - if(iRespring)AccessMenu(kAv,iAuth); } else if(sMenu == "Menu~Settings"){ if(sMsg == UPMENU){ iRespring=FALSE; - Menu(kAv, iAuth); + Dialog(kAv,"",[],[],0,iAuth,"Menu~Main"); } else if(sMsg == Checkbox(g_iLimitRange, "Limit Range")){ if(iAuth >=CMD_OWNER && iAuth <= CMD_TRUSTED){ g_iLimitRange=1-g_iLimitRange; @@ -647,10 +654,8 @@ state active iRespring=FALSE; } else if(sMsg == "Addon.."){ iRespring=FALSE; - AddonSettings(kAv,iAuth); + Dialog(kAv,"",[],[],0,iAuth,"Menu~SAddons"); } - - if(iRespring)Settings(kAv,iAuth); }else if(sMenu == "Menu~SAddons"){ if(sMsg == Checkbox(g_iWearerAddons, "WearerAddons")){ if(iAuth == CMD_OWNER || iAuth == CMD_TRUSTED){ @@ -673,16 +678,12 @@ state active }else llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to toggling all addons", kAv); }else if(sMsg == UPMENU){ iRespring=FALSE; - Settings(kAv,iAuth); + Dialog(kAv,"",[],[],0,iAuth,"Menu~Settings"); } - - - - if(iRespring)AddonSettings(kAv,iAuth); } else if(sMenu == "Menu~Help"){ if(sMsg == UPMENU){ iRespring=FALSE; - Menu(kAv,iAuth); + Dialog(kAv,"",[],[],0,iAuth,"Menu~Main"); } else if(sMsg == "Reboot") { llMessageLinked(LINK_SET, iAuth, "Reboot", kAv); } else if(sMsg == "License"){ @@ -692,14 +693,13 @@ state active } else if(sMsg == "Update"){ UserCommand(iAuth, "update", kAv); } - - if(iRespring)HelpMenu(kAv,iAuth); } else if(sMenu == "Menu~Apps"){ if(sMsg == UPMENU){ - Menu(kAv, iAuth); + Dialog(kAv,"",[],[],0,iAuth,"Menu~Main"); }else{ llMessageLinked(LINK_SET, 0, "menu "+sMsg, kAv); } + iRespring=FALSE; } else if(sMenu == "Update~Confirm") { if(sMsg == "Yes"){ @@ -711,11 +711,10 @@ state active g_iDoTriggerUpdate=FALSE; g_kUpdater=NULL_KEY; } + iRespring=FALSE; } + if(iRespring)Dialog(kAv,"",[],[],0,iAuth,sMenu); } - }else if (iNum == DIALOG_TIMEOUT) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex +3); //remove stride from g_lMenuIDs } else if(iNum == LM_SETTING_RESPONSE){ list lPar = llParseString2List(sStr, ["_","="],[]); string sToken = llList2String(lPar,0); @@ -738,6 +737,9 @@ state active } } else if(sVar == "safeword"){ g_sSafeword = sVal; + + } else if(sVar == "safeworddisable"){ + g_iSafewordDisable=1; } else if(sVar == "prefix"){ g_sPrefix = sVal; } else if(sVar == "channel"){ @@ -807,7 +809,9 @@ state active g_iLocked=FALSE; llOwnerSay("@detach=y"); } - else if(sVar == "safeword"){ + else if(sVar == "safeworddisable"){ + g_iSafewordDisable=0; + } else if(sVar == "safeword"){ g_sSafeword = "RED"; llMessageLinked(LINK_SET, CMD_OWNER, "safeword-enable",""); } else if(sVar == "prefix"){ @@ -841,7 +845,7 @@ state active } else if(iNum == AUTH_REPLY){ if(kID == "welder_auth_check"){ //pop menu for welder list lParameters = llParseString2List(sStr, ["|"],[]); - Menu(g_kWeldBy,llList2Integer(lParameters,2)); + Dialog(g_kWeldBy,"",[],[],0,llList2Integer(lParameters,2),"Menu~Main"); llMessageLinked(LINK_SET, NOTIFY_OWNERS, "%WEARERNAME%'s collar has been welded", g_kWelder); llMessageLinked(LINK_SET, NOTIFY, "1Weld completed", g_kWearer); //We shouldn't have to send this to the welder. Welder should always be an owner. } @@ -953,4 +957,3 @@ state active } } } - diff --git a/src/collar/oc_couples.lsl b/src/collar/oc_couples.lsl index 0056b155b..0f4e29a52 100644 --- a/src/collar/oc_couples.lsl +++ b/src/collar/oc_couples.lsl @@ -1,18 +1,22 @@ // This file is part of OpenCollar. // Copyright (c) 2004 - 2021 Francis Chung, Ilse Mannonen, Nandana Singh, // Cleo Collins, Satomi Ahn, Joy Stipe, Wendy Starfall, Garvin Twine, -// littlemousy, Romka Swallowtail, Sumi Perl et al. +// littlemousy, Romka Swallowtail, Sumi Perl, Rosalyn Russell et al. + +//Rosalyn (RosalynRussell) +// *Apr 2024 - Added optional partner controlled couples pose system to remove legacy ask box if initiator is trusted + // Licensed under the GPLv2. See LICENSE for full details. -string g_sScriptVersion="8.1"; +string g_sScriptVersion="8.3"; integer LINK_CMD_DEBUG=1999; DebugOutput(key kID, list ITEMS){ - integer i=0; - integer end=llGetListLength(ITEMS); - string final; - for(i=0;i 0){ - //Debug("Anim timeout="+(string)g_iAnimTimeout+"\ntime now="+(string)timeNow); - g_iAnimTimeout=0; - StopAnims(); - } else if (g_iPermissionTimeout <= timeNow && g_iPermissionTimeout > 0){ - //Debug("Perm timeout="+(string)g_iPermissionTimeout+"\ntime now="+(string)timeNow); - g_iPermissionTimeout=0; - llListenRemove(g_iListener); - g_kPartner = NULL_KEY; - } - integer nextTimeout=g_iAnimTimeout; - if (g_iPermissionTimeout < g_iAnimTimeout && g_iPermissionTimeout > 0) - nextTimeout = g_iPermissionTimeout; - llSetTimerEvent(nextTimeout-timeNow); + integer timeNow = llGetUnixTime(); + if (g_iAnimTimeout <= timeNow && g_iAnimTimeout > 0){ + //Debug("Anim timeout="+(string)g_iAnimTimeout+"\ntime now="+(string)timeNow); + g_iAnimTimeout=0; + StopAnims(); + } else if (g_iPermissionTimeout <= timeNow && g_iPermissionTimeout > 0){ + //Debug("Perm timeout="+(string)g_iPermissionTimeout+"\ntime now="+(string)timeNow); + g_iPermissionTimeout=0; + llListenRemove(g_iListener); + g_kPartner = NULL_KEY; + } + integer nextTimeout=g_iAnimTimeout; + if (g_iPermissionTimeout < g_iAnimTimeout && g_iPermissionTimeout > 0) + nextTimeout = g_iPermissionTimeout; + llSetTimerEvent(nextTimeout-timeNow); } CoupleAnimMenu(key kID, integer iAuth) { - string sPrompt = "\n[Couples]\n\nChoose an animation to play.\n\nAnimations will play "; - if(g_fTimeOut == 0) sPrompt += "ENDLESS.\n\nNOTE: The non-looped \"pet\" sequence is an exception to this rule and can only play for 20 seconds at a time." ; - else sPrompt += "for "+(string)llCeil(g_fTimeOut)+" seconds."; - list lButtons = g_lAnimCmds; - lButtons += [TIME_COUPLES, STOP_COUPLES]; - Dialog(kID, sPrompt, lButtons, [UPMENU],0, iAuth,"couples"); + string sPrompt = "\n[Couples]\n\nChoose an animation to play.\nAnimations will play "; + if(g_fTimeOut == 0) sPrompt += "ENDLESS.\n\nNOTE: The non-looped \"pet\" sequence is an exception to this rule and can only play for 20 seconds at a time."; + else sPrompt += "for "+(string)llCeil(g_fTimeOut)+" seconds."; + sPrompt += "\n\nAuto Acc. will auto accept animation permission from trusted or owners."; + list lButtons = g_lAnimCmds; + lButtons += [TIME_COUPLES, STOP_COUPLES]; + if(g_iAutomate) lButtons+="▣ Auto Acc."; else lButtons+="▢ Auto Acc."; + Dialog(kID, sPrompt, lButtons, [UPMENU],0, iAuth,"couples"); } string StrReplace(string sSrc, string sFrom, string sTo) { //replaces all occurrences of 'from' with 'to' in 'sSrc'. - integer iLength = (~-(llStringLength(sFrom))); - if(~iLength) { - string sBuffer = sSrc; - integer b_pos = -1; - integer to_len = (~-(llStringLength(sTo))); - @loop;//instead of a while loop, saves 5 bytes (and run faster). - integer to_pos = ~llSubStringIndex(sBuffer, sFrom); - if(to_pos) { - b_pos -= to_pos; - sSrc = llInsertString(llDeleteSubString(sSrc, b_pos, b_pos + iLength), b_pos, sTo); - b_pos += to_len; - sBuffer = llGetSubString(sSrc, (-~(b_pos)), 0x8000); - //buffer = llGetSubString(sSrc = llInsertString(llDeleteSubString(sSrc, b_pos -= to_pos, b_pos + len), b_pos, to), (-~(b_pos += to_len)), 0x8000); - jump loop; - } - } - return sSrc; + integer iLength = (~-(llStringLength(sFrom))); + if(~iLength) { + string sBuffer = sSrc; + integer b_pos = -1; + integer to_len = (~-(llStringLength(sTo))); + @loop;//instead of a while loop, saves 5 bytes (and run faster). + integer to_pos = ~llSubStringIndex(sBuffer, sFrom); + if(to_pos) { + b_pos -= to_pos; + sSrc = llInsertString(llDeleteSubString(sSrc, b_pos, b_pos + iLength), b_pos, sTo); + b_pos += to_len; + sBuffer = llGetSubString(sSrc, (-~(b_pos)), 0x8000); + //buffer = llGetSubString(sSrc = llInsertString(llDeleteSubString(sSrc, b_pos -= to_pos, b_pos + len), b_pos, to), (-~(b_pos += to_len)), 0x8000); + jump loop; + } + } + return sSrc; } //added to stop eventual still going animations StopAnims() { - if (llGetInventoryType(g_sSubAnim) == INVENTORY_ANIMATION) llMessageLinked(LINK_SET, ANIM_STOP, g_sSubAnim, ""); - if (llGetInventoryType(g_sDomAnim) == INVENTORY_ANIMATION) { - if (llKey2Name(g_kPartner) != "") { - llStopAnimation(g_sDomAnim); - llRegionSayTo(g_kPartner,g_iLMChannel,(string)g_kPartner+"booton"); - } - } - g_sSubAnim = ""; - g_sDomAnim = ""; + if (llGetInventoryType(g_sSubAnim) == INVENTORY_ANIMATION) llMessageLinked(LINK_SET, ANIM_STOP, g_sSubAnim, ""); + if (llGetInventoryType(g_sDomAnim) == INVENTORY_ANIMATION) { + if (llKey2Name(g_kPartner) != "") + { + if (anim_powered == "PARTNER") + { + integer iChannel = getChannel(g_kPartner); + llSay(iChannel, (string)g_kPartner + ":partnerstop "+g_sDomAnim); + } + else + { + llStopAnimation(g_sDomAnim); + } + llRegionSayTo(g_kPartner,g_iLMChannel,(string)g_kPartner+"booton"); + } + } + g_sSubAnim = ""; + g_sDomAnim = ""; + anim_powered = ""; } // Calmly walk up to your partner and face them. Does not position the avatar precicely MoveToPartner() { - list partnerDetails = llGetObjectDetails(g_kPartner, [OBJECT_POS, OBJECT_ROT]); - vector partnerPos = llList2Vector(partnerDetails, 0); - rotation partnerRot = llList2Rot(partnerDetails, 1); - vector partnerEuler = llRot2Euler(partnerRot); - // turn to face the partner - llMessageLinked(LINK_SET, RLV_CMD, "setrot:" + (string)(-PI_BY_TWO-partnerEuler.z) + "=force", NULL_KEY); - - g_iTargetID = llTarget(partnerPos, g_fWalkingDistance); - llMoveToTarget(partnerPos, g_fWalkingTau); + list partnerDetails = llGetObjectDetails(g_kPartner, [OBJECT_POS, OBJECT_ROT]); + vector partnerPos = llList2Vector(partnerDetails, 0); + rotation partnerRot = llList2Rot(partnerDetails, 1); + vector partnerEuler = llRot2Euler(partnerRot); + // turn to face the partner + llMessageLinked(LINK_SET, RLV_CMD, "setrot:" + (string)(-PI_BY_TWO-partnerEuler.z) + "=force", NULL_KEY); + + g_iTargetID = llTarget(partnerPos, g_fWalkingDistance); + llMoveToTarget(partnerPos, g_fWalkingTau); } GetPartnerPermission() { - string sObjectName = llGetObjectName(); - llSetObjectName(g_sDeviceName); - llRequestPermissions(g_kPartner, PERMISSION_TRIGGER_ANIMATION); - llSetObjectName(sObjectName); + string sObjectName = llGetObjectName(); + //g_sSubAnim = llList2String(g_lAnimSettings, g_iCmdIndex * 4); + string temp_g_sDomAnim = llList2String(g_lAnimSettings, g_iCmdIndex * 4 + 1); + llSetObjectName(g_sDeviceName); + //Ping their oc_couples_partner script + ping_sent_to_key = g_kPartner; + integer iChannel = getChannel(g_kPartner); + llSay(iChannel, (string)g_kPartner + ":partnerping "+temp_g_sDomAnim); + //Set Timeout + timer_reason = "PING"; + llSetTimerEvent(2.5); + //llRequestPermissions(g_kPartner, PERMISSION_TRIGGER_ANIMATION); + //llSetObjectName(sObjectName); } StartNotecards() { - if (llGetInventoryType(CARD1) == INVENTORY_NOTECARD) { //card is present, start reading - g_kCardID1 = llGetInventoryKey(CARD1); - g_iLine1 = 0; - g_lAnimCmds = []; - g_lAnimSettings = []; - g_kDataID1 = llGetNotecardLine(CARD1, g_iLine1); - } - if (llGetInventoryType(CARD2) == INVENTORY_NOTECARD) { //card is present, start reading - g_kCardID2 = llGetInventoryKey(CARD2); - g_iLine2 = 0; - g_kDataID2 = llGetNotecardLine(CARD2, g_iLine2); - } + if (llGetInventoryType(CARD1) == INVENTORY_NOTECARD) { //card is present, start reading + g_kCardID1 = llGetInventoryKey(CARD1); + g_iLine1 = 0; + g_lAnimCmds = []; + g_lAnimSettings = []; + g_kDataID1 = llGetNotecardLine(CARD1, g_iLine1); + } + if (llGetInventoryType(CARD2) == INVENTORY_NOTECARD) { //card is present, start reading + g_kCardID2 = llGetInventoryKey(CARD2); + g_iLine2 = 0; + g_kDataID2 = llGetNotecardLine(CARD2, g_iLine2); + } } integer ALIVE = -55; @@ -240,318 +281,446 @@ integer READY = -56; integer STARTUP = -57; default { - on_rez(integer iNum){ - llResetScript(); - } - state_entry(){ - llMessageLinked(LINK_SET, ALIVE, llGetScriptName(),""); - } - link_message(integer iSender, integer iNum, string sStr, key kID){ - if(iNum == REBOOT){ - if(sStr == "reboot"){ - llResetScript(); - } - } else if(iNum == READY){ - llMessageLinked(LINK_SET, ALIVE, llGetScriptName(), ""); - } else if(iNum == STARTUP){ - state active; - } - } + on_rez(integer iNum){ + llResetScript(); + } + state_entry(){ + llMessageLinked(LINK_SET, ALIVE, llGetScriptName(),""); + } + link_message(integer iSender, integer iNum, string sStr, key kID){ + if(iNum == REBOOT){ + if(sStr == "reboot"){ + llResetScript(); + } + } else if(iNum == READY){ + llMessageLinked(LINK_SET, ALIVE, llGetScriptName(), ""); + } else if(iNum == STARTUP){ + state active; + } + } } state active { - on_rez(integer iStart) - { - //added to stop anims after relog when you logged off while in an endless couple anim - if (g_sSubAnim != "" && g_sDomAnim != "") { - llSleep(1.0); // wait a second to make sure the poses script reseted properly - StopAnims(); - } - llResetScript(); - } - - state_entry() { - g_kWearer = llGetOwner(); - StartNotecards(); - g_sDeviceName = llList2String(llGetLinkPrimitiveParams(1,[PRIM_NAME]),0); - //Debug("Starting"); - } - - listen(integer iChannel, string sName, key kID, string sMessage) { - //Debug("listen: " + sMessage + ", iChannel=" + (string)channel); - llListenRemove(g_iListener); - if (iChannel == g_iStopChan) StopAnims(); - } - - link_message(integer iSender, integer iNum, string sStr, key kID){ - //if you don't care who gave the command, so long as they're one of the above, you can just do this instead: - if (iNum >= CMD_OWNER && iNum <= CMD_EVERYONE) { - //the command was given by either owner, secowner, group member, wearer, or public user - list lParams = llParseString2List(sStr, [" "], []); - g_kCmdGiver = kID; - g_iCmdAuth = iNum; - string sCommand = llToLower(llList2String(lParams, 0)); - string sValue = llToLower(llList2String(lParams, 1)); - integer tmpiIndex = llListFindList(g_lAnimCmds, [sCommand]); - if (tmpiIndex != -1) { //if the couple anim exists - g_iCmdIndex = tmpiIndex; - //Debug(sCommand); - //we got an anim command. - if (llGetListLength(lParams) > 1) {//we've been given a name of someone to kiss. scan for it - string sTmpName = llDumpList2String(llList2List(lParams, 1, -1), " ");//this makes it so we support even full names in the command - //g_kPart=llGenerateKey(); - //llMessageLinked(LINK_SET, SENSORDIALOG, (string)g_kCmdGiver + "|\nChoose a partner:\n|0|``"+(string)AGENT+"`"+(string)g_fRange+"`"+(string)PI +"`"+sTmpName+"`1"+ "|BACK|" + (string)iNum, g_kPart); - Dialog(g_kCmdGiver, "\nChoose a partner:\n", [sTmpName], ["BACK"], 0, iNum, "sensor"); - } else { //no name given. - if (kID == g_kWearer) { //if commander is not sub, then treat commander as partner - llMessageLinked(LINK_SET, NOTIFY, - "0"+"\n\nYou didn't give the name of the person you want to animate. To " + sCommand + - " Alice Mannonen, for example, you could say:\n\n /%CHANNEL% %PREFIX%" + sCommand + " ali\n", g_kWearer); - } else { //else set partner to commander - g_kPartner = g_kCmdGiver; - g_sPartnerName = "secondlife:///app/agent/"+(string)g_kPartner+"/about"; - //added to stop eventual still going animations - StopAnims(); - GetPartnerPermission(); - llMessageLinked(LINK_SET,NOTIFY,"0"+"Offering to " + sCommand + " " + g_sPartnerName + ".",g_kWearer); - } - } - } else if (llToLower(sStr) == "stop couples") StopAnims(); - else if (llToLower(sStr) == "menu "+llToLower(g_sSubMenu) || llToLower(sStr) == "couples") CoupleAnimMenu(kID, iNum); - else if (sCommand == "couples" && sValue == "verbose") { - sValue = llToLower(llList2String(lParams, 2)); - if (sValue == "off"){ - g_iVerbose = FALSE; - llMessageLinked(LINK_SET, LM_SETTING_SAVE, g_sSettingToken + "verbose=" + (string)g_iVerbose, ""); - } else if (sValue == "on") { - g_iVerbose = TRUE; - llMessageLinked(LINK_SET, LM_SETTING_DELETE, g_sSettingToken + "verbose", ""); - } - llMessageLinked(LINK_SET,NOTIFY,"0"+"Verbose for couple animations is now turned "+sValue+".",kID); - } - } else if (iNum == MENUNAME_REQUEST && sStr == g_sParentMenu) - llMessageLinked(iSender, MENUNAME_RESPONSE, g_sParentMenu + "|" + g_sSubMenu, ""); - else if (iNum == LM_SETTING_RESPONSE) { - list lParams = llParseString2List(sStr, ["="], []); - string sToken = llList2String(lParams, 0); - string sValue = llList2String(lParams, 1); - - //integer ind = llListFindList(g_lSettingsReqs, [sToken]); - //if(ind!=-1)g_lSettingsReqs = llDeleteSubList(g_lSettingsReqs, ind,ind); - - if(sToken == g_sSettingToken + "timeout") - g_fTimeOut = (float)sValue; - else if (sToken == g_sSettingToken + "verbose") - g_iVerbose = (integer)sValue; - else if (sToken == g_sGlobalToken+"devicename") - g_sDeviceName = sValue; - } else if(iNum == LM_SETTING_EMPTY){ - - //integer ind = llListFindList(g_lSettingsReqs, [sStr]); - //if(ind!=-1)g_lSettingsReqs = llDeleteSubList(g_lSettingsReqs, ind,ind); - - } else if(iNum == LM_SETTING_DELETE){ - - //integer ind = llListFindList(g_lSettingsReqs, [sStr]); - //if(ind!=-1)g_lSettingsReqs = llDeleteSubList(g_lSettingsReqs, ind,ind); - - } else if (iNum == DIALOG_RESPONSE) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - if (~iMenuIndex) { - list lMenuParams = llParseString2List(sStr, ["|"], []); - key kAv = (key)llList2String(lMenuParams, 0); - string sMessage = llList2String(lMenuParams, 1); - // integer iPage = (integer)llList2String(lMenuParams, 2); - integer iAuth = (integer)llList2String(lMenuParams, 3); - string sMenu=llList2String(g_lMenuIDs, iMenuIndex + 1); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex - 2 + g_iMenuStride); - if (sMenu == "couples") { - if (sMessage == UPMENU) - llMessageLinked(LINK_SET, iAuth, "menu " + g_sParentMenu, kAv); - else if (sMessage == STOP_COUPLES) { - StopAnims(); - CoupleAnimMenu(kAv, iAuth); - } else if (sMessage == TIME_COUPLES) { - string sPrompt = "\nChoose the duration for couple animations.\n\nCurrent duration: "; - if(g_fTimeOut == 0) sPrompt += "ENDLESS.\n\nNOTE: The non-looped \"pet\" sequence is an exception to this rule and can only play for 20 seconds at a time." ; - else sPrompt += "for "+(string)llCeil(g_fTimeOut)+" seconds."; - Dialog(kAv, sPrompt, ["10","20","30","40","60","90","120", "ENDLESS"], [UPMENU],0, iAuth,"timer"); - } else if (llGetSubString(sMessage,0,6) == "Verbose") { - if (llGetSubString(sMessage,8,-1) == "Off") { - g_iVerbose = FALSE; - llMessageLinked(LINK_SET, LM_SETTING_SAVE, g_sSettingToken + "verbose=" + (string)g_iVerbose, ""); - } else { - g_iVerbose = TRUE; - llMessageLinked(LINK_SET, LM_SETTING_DELETE, g_sSettingToken + "verbose", ""); - } - CoupleAnimMenu(kAv, iAuth); - } else { - integer iIndex = llListFindList(g_lAnimCmds, [sMessage]); - if (iIndex != -1) { - g_kCmdGiver = kAv; - g_iCmdAuth = iAuth; - g_iCmdIndex = iIndex; - //llSensor("", NULL_KEY, AGENT, g_fRange, PI); - Dialog(g_kCmdGiver, "\nChoose a partner:\n", [], ["BACK"], 0, iNum, "sensor"); - //g_kPart=llGenerateKey(); - //llMessageLinked(LINK_SET, SENSORDIALOG, (string)g_kCmdGiver + "|\nChoose a partner:\n|0|``"+(string)AGENT+"`"+(string)g_fRange+"`"+(string)PI + "|BACK|" + (string)iAuth, g_kPart); - } - } - } else if (sMenu == "sensor") { - //Debug("Response from partner"+sStr); - if (sMessage == UPMENU) CoupleAnimMenu(kAv, iAuth); - else { - g_kPartner = (key)sMessage; - g_sPartnerName = "secondlife:///app/agent/"+(string)g_kPartner+"/about"; - StopAnims(); - GetPartnerPermission(); - llMessageLinked(LINK_SET,NOTIFY,"0"+"Inviting "+ g_sPartnerName + " to a couples animation.",g_kWearer); - llMessageLinked(LINK_SET,NOTIFY,"0"+"%WEARERNAME% invited you to a couples animation! Click [Yes] to accept.",g_kPartner); - } - } else if (sMenu == "timer") { - //Debug("Response from timer menu"+sStr); - if (sMessage == UPMENU) CoupleAnimMenu(kAv, iAuth); - else if ((integer)sMessage > 0 && ((string)((integer)sMessage) == sMessage)) { - g_fTimeOut = (float)((integer)sMessage); - llMessageLinked(LINK_SET, LM_SETTING_SAVE, g_sSettingToken + "timeout=" + (string)g_fTimeOut, ""); - string sPet; - if (g_fTimeOut > 20.0) sPet = "(except the \"pet\" sequence) "; - llMessageLinked(LINK_SET,NOTIFY,"1"+"Couple Anmiations "+sPet+"play now for " + (string)llRound(g_fTimeOut) + " seconds.",kAv); - CoupleAnimMenu(kAv, iAuth); - } else if (sMessage == "ENDLESS") { - g_fTimeOut = 0.0; - llMessageLinked(LINK_SET, LM_SETTING_SAVE, g_sSettingToken + "timeout=0.0", ""); - llMessageLinked(LINK_SET,NOTIFY,"1"+"Couple Anmiations (except the \"pet\" sequence) play now forever. Use the menu or type \"/%CHANNEL% %PREFIX% stop couples\" to stop them again.",kAv); - CoupleAnimMenu(kAv, iAuth); - } - } - } - } else if (iNum == DIALOG_TIMEOUT) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex +3); //remove stride from g_lMenuIDs - } else if (iNum == LOADPIN && sStr == llGetScriptName()) { - integer iPin = (integer)llFrand(99999.0)+1; - llSetRemoteScriptAccessPin(iPin); - llMessageLinked(iSender, LOADPIN, (string)iPin+"@"+llGetScriptName(),llGetKey()); - } - else if (iNum == REBOOT && sStr == "reboot") llResetScript(); - else if(iNum == LINK_CMD_DEBUG){ - integer onlyver=0; - if(sStr == "ver")onlyver=1; - llInstantMessage(kID, llGetScriptName() +" SCRIPT VERSION: "+g_sScriptVersion); - if(onlyver)return; // basically this command was: versions - DebugOutput(kID, [" PARTNER:", g_kPartner]); - } - } - not_at_target() { - llTargetRemove(g_iTargetID); - MoveToPartner(); - } - - at_target(integer tiNum, vector targetpos, vector ourpos) { - llTargetRemove(tiNum); - llStopMoveToTarget(); - float offset = 10.0; - if (g_iCmdIndex != -1) offset = (float)llList2String(g_lAnimSettings, g_iCmdIndex * 4 + 2); - list partnerDetails = llGetObjectDetails(g_kPartner, [OBJECT_POS, OBJECT_ROT]); - vector partnerPos = llList2Vector(partnerDetails, 0); - rotation partnerRot = llList2Rot(partnerDetails, 1); - vector myPos = llList2Vector(llGetObjectDetails(llGetOwner(), [OBJECT_POS]), 0); - - vector target = partnerPos + (<1.0, 0.0, 0.0> * partnerRot * offset); // target is meters in front of the partner - target.z = myPos.z; // ignore height differences - llMoveToTarget(target, g_fAlignTau); - llSleep(g_fAlignDelay); - llStopMoveToTarget(); - g_sSubAnim = llList2String(g_lAnimSettings, g_iCmdIndex * 4); - g_sDomAnim = llList2String(g_lAnimSettings, g_iCmdIndex * 4 + 1); - - llMessageLinked(LINK_SET, ANIM_START, g_sSubAnim, ""); - llRegionSayTo(g_kPartner,g_iLMChannel,(string)g_kPartner+"bootoff"); - llStartAnimation(g_sDomAnim); - g_iListener = llListen(g_iStopChan, "", g_kPartner, g_sStopString); - llMessageLinked(LINK_SET,NOTIFY,"0"+"If you would like to stop the animation early, say /" + (string)g_iStopChan + g_sStopString + " to stop.",g_kPartner); - - string sText = llList2String(g_lAnimSettings, g_iCmdIndex * 4 + 3); - if (sText != "" && g_iVerbose) { - sText = StrReplace(sText,"_PARTNER_",g_sPartnerName); - sText = StrReplace(sText,"_SELF_","%WEARERNAME%"); - llMessageLinked(LINK_SET,SAY,"0"+sText,""); - } - if (g_fTimeOut > 0.0) { - g_iAnimTimeout=llGetUnixTime()+(integer)g_fTimeOut; - if (g_sSubAnim == "~good" && g_fTimeOut > 20.0) g_iAnimTimeout = llGetUnixTime()+20; - } else if (g_sSubAnim == "~good") g_iAnimTimeout = llGetUnixTime()+20; - else g_iAnimTimeout=0; - refreshTimer(); - } - timer() { - refreshTimer(); - } - dataserver(key kID, string sData) { - if (sData == EOF) iCardComplete++; - else { - list lParams = llParseString2List(sData, ["|"], []); - integer iLength = llGetListLength(lParams); - if (iLength == 4 || iLength == 5) { - if (!llGetInventoryType(llList2String(lParams, 1)) == INVENTORY_ANIMATION){ - llMessageLinked(LINK_SET,NOTIFY,"0"+CARD1 + " line " + (string)g_iLine1 + ": animation '" + llList2String(lParams, 1) + "' is not present. Skipping.",g_kWearer); - } else if (!llGetInventoryType(llList2String(lParams, 2)) == INVENTORY_ANIMATION){ - llMessageLinked(LINK_SET,NOTIFY,"0"+CARD1 + " line " + (string)g_iLine2 + ": animation '" + llList2String(lParams, 2) + "' is not present. Skipping.",g_kWearer); - } else { - integer iIndex = llListFindList(g_lAnimCmds, llList2List(lParams, 0, 0)); - if (~iIndex) { - g_lAnimCmds=llDeleteSubList(g_lAnimCmds,iIndex,iIndex); - g_lAnimSettings=llDeleteSubList(g_lAnimSettings,iIndex*4,iIndex*4+3); - } - g_lAnimCmds += llList2List(lParams, 0, 0); - g_lAnimSettings += llList2List(lParams, 1, 3); - g_lAnimSettings += [llList2String(lParams, 4)]; - //Debug(llDumpList2String(g_lAnimCmds, ",")); - //Debug(llDumpList2String(g_lAnimSettings, ",")); - } - } - if ( iCardComplete <2) { - if (kID == g_kDataID1) { - if (llGetInventoryType(CARD1) == INVENTORY_NOTECARD) { - g_iLine1++; - g_kDataID1 = llGetNotecardLine(CARD1, g_iLine1); - } - } else if (kID == g_kDataID2) { - if (llGetInventoryType(CARD2) == INVENTORY_NOTECARD) { - g_iLine2++; - g_kDataID2 = llGetNotecardLine(CARD2, g_iLine2); - } - } - } - } - } - - run_time_permissions(integer perm) { - if (perm & PERMISSION_TRIGGER_ANIMATION) { - key kID = llGetPermissionsKey(); - //Debug("changed anim permissions\nPerm ID="+(string)kID+"g_kPartner="+(string)g_kPartner); - if (kID == g_kPartner) { - g_iPermissionTimeout=0; - MoveToPartner(); - } else { - llMessageLinked(LINK_SET,NOTIFY,"1"+"Sorry, but the request timed out.",kID); - } - } - } - - changed(integer iChange) { - if (iChange & CHANGED_INVENTORY) { - if (llGetInventoryKey(CARD1) != g_kCardID1) StartNotecards(); - if (llGetInventoryKey(CARD2) != g_kCardID2) StartNotecards(); - } + on_rez(integer iStart) + { + //added to stop anims after relog when you logged off while in an endless couple anim + if (g_sSubAnim != "" && g_sDomAnim != "") { + llSleep(1.0); // wait a second to make sure the poses script reseted properly + auto_busy = FALSE; + StopAnims(); + } + llResetScript(); + } + + state_entry() { + g_kWearer = llGetOwner(); + StartNotecards(); + g_sDeviceName = llList2String(llGetLinkPrimitiveParams(1,[PRIM_NAME]),0); + //Debug("Starting"); + } + + listen(integer iChannel, string sName, key kID, string sMessage) + { + //Debug("listen: " + sMessage + ", iChannel=" + (string)channel); + llListenRemove(g_iListener); + if (iChannel == g_iStopChan) StopAnims(); + } + + link_message(integer iSender, integer iNum, string sStr, key kID){ + if ((kID == ping_sent_to_key)&&(kID != NULL_KEY))//Is this from the person we sent a ping to? + { + list lParams = llParseString2List(sStr, [" "], []); + string sCommand = llToLower(llList2String(lParams, 0)); + string sValue = llToLower(llList2String(lParams, 1)); + g_kCmdGiver = kID; + g_kPartner = g_kCmdGiver; + g_sPartnerName = "secondlife:///app/agent/"+(string)g_kPartner+"/about"; + if (sCommand == "partnerpong") + { + timer_reason = ""; + llSetTimerEvent(0.0); + anim_powered = "PARTNER"; + MoveToPartner(); + } + else if (sCommand == "partnererror") + { + if (sValue == "autooff") + { + timer_reason = ""; + llSetTimerEvent(0.0); + llMessageLinked(LINK_SET,NOTIFY,"0"+g_sPartnerName + " has auto accept disabled, using legacy permissions instead.",g_kWearer); + llMessageLinked(LINK_SET,NOTIFY,"0"+"%WEARERNAME% invited you to a couples animation! Click [Yes] to accept.",g_kPartner); + llRequestPermissions(g_kPartner, PERMISSION_TRIGGER_ANIMATION); + } + if (sValue == "autobusy") + { + timer_reason = ""; + llSetTimerEvent(0.0); + llMessageLinked(LINK_SET,NOTIFY,"0"+g_sPartnerName + " is currently being animated, please wait.",g_kWearer); + //llMessageLinked(LINK_SET,NOTIFY,"0"+"%WEARERNAME% invited you to a couples animation! Click [Yes] to accept.",g_kPartner); + //llRequestPermissions(g_kPartner, PERMISSION_TRIGGER_ANIMATION); + } + } + } + //if you don't care who gave the command, so long as they're one of the above, you can just do this instead: + if (iNum >= CMD_OWNER && iNum <= CMD_EVERYONE) { + //the command was given by either owner, secowner, group member, wearer, or public user + list lParams = llParseString2List(sStr, [" "], []); + g_kCmdGiver = kID; + g_iCmdAuth = iNum; + string sCommand = llToLower(llList2String(lParams, 0)); + string sValue = llToLower(llList2String(lParams, 1)); + integer tmpiIndex = llListFindList(g_lAnimCmds, [sCommand]); + if (tmpiIndex != -1) { //if the couple anim exists + g_iCmdIndex = tmpiIndex; + //Debug(sCommand); + //we got an anim command. + if (llGetListLength(lParams) > 1) {//we've been given a name of someone to kiss. scan for it + string sTmpName = llDumpList2String(llList2List(lParams, 1, -1), " ");//this makes it so we support even full names in the command + //g_kPart=llGenerateKey(); + //llMessageLinked(LINK_SET, SENSORDIALOG, (string)g_kCmdGiver + "|\nChoose a partner:\n|0|``"+(string)AGENT+"`"+(string)g_fRange+"`"+(string)PI +"`"+sTmpName+"`1"+ "|BACK|" + (string)iNum, g_kPart); + Dialog(g_kCmdGiver, "\nChoose a partner:\n", [sTmpName], ["BACK"], 0, iNum, "sensor"); + } else { //no name given. + if (kID == g_kWearer) { //if commander is not sub, then treat commander as partner + llMessageLinked(LINK_SET, NOTIFY, + "0"+"\n\nYou didn't give the name of the person you want to animate. To " + sCommand + + " Alice Mannonen, for example, you could say:\n\n /%CHANNEL% %PREFIX%" + sCommand + " ali\n", g_kWearer); + } else { //else set partner to commander + g_kPartner = g_kCmdGiver; + g_sPartnerName = "secondlife:///app/agent/"+(string)g_kPartner+"/about"; + //added to stop eventual still going animations + StopAnims(); + GetPartnerPermission(); + llMessageLinked(LINK_SET,NOTIFY,"0"+"Offering to " + sCommand + " " + g_sPartnerName + ".",g_kWearer); + } + } + } else if (llToLower(sStr) == "stop couples") StopAnims(); + else if (llToLower(sStr) == "menu "+llToLower(g_sSubMenu) || llToLower(sStr) == "couples") CoupleAnimMenu(kID, iNum); + else if (sCommand == "couples" && sValue == "verbose") { + sValue = llToLower(llList2String(lParams, 2)); + if (sValue == "off"){ + g_iVerbose = FALSE; + llMessageLinked(LINK_SET, LM_SETTING_SAVE, g_sSettingToken + "verbose=" + (string)g_iVerbose, ""); + } else if (sValue == "on") { + g_iVerbose = TRUE; + llMessageLinked(LINK_SET, LM_SETTING_DELETE, g_sSettingToken + "verbose", ""); + } + llMessageLinked(LINK_SET,NOTIFY,"0"+"Verbose for couple animations is now turned "+sValue+".",kID); + } + else if (sCommand == "partnerping") + { + if(llGetInventoryType(sValue)==INVENTORY_ANIMATION) + { + if (g_iAutomate == FALSE) + { + integer iChannel = getChannel(g_kCmdGiver); + llSay(iChannel, (string)g_kCmdGiver + ":partnererror autooff"); + } + else if (auto_busy == TRUE) + { + integer iChannel = getChannel(g_kCmdGiver); + llSay(iChannel, (string)g_kCmdGiver + ":partnererror autobusy"); + } + else + { + //vector pointTo = llList2Vector(llGetObjectDetails(llGetOwnerKey(id),[OBJECT_POS]),0) - llGetPos(); + auto_busy = TRUE; + vector pointTo = llList2Vector(llGetObjectDetails(g_kCmdGiver,[OBJECT_POS]),0) - llGetPos(); + float turnAngle = llAtan2(pointTo.x, pointTo.y);// - myAngle; + llOwnerSay("@setrot:" + (string)(turnAngle) + "=force"); + llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION); + } + } + } + else if (sCommand == "partnerpose") + { + g_sPartnerAnim = sValue; + //llStartAnimation(g_sPartnerAnim); + llMessageLinked(LINK_SET, ANIM_START, g_sPartnerAnim, ""); + } + else if (sCommand == "partnerstop") + { + g_sPartnerAnim = sValue; + auto_busy = FALSE; + //llStopAnimation(g_sPartnerAnim); + llMessageLinked(LINK_SET, ANIM_STOP, g_sPartnerAnim, ""); + } + } else if (iNum == MENUNAME_REQUEST && sStr == g_sParentMenu) + llMessageLinked(iSender, MENUNAME_RESPONSE, g_sParentMenu + "|" + g_sSubMenu, ""); + else if (iNum == LM_SETTING_RESPONSE) { + list lParams = llParseString2List(sStr, ["="], []); + string sToken = llList2String(lParams, 0); + string sValue = llList2String(lParams, 1); + + //integer ind = llListFindList(g_lSettingsReqs, [sToken]); + //if(ind!=-1)g_lSettingsReqs = llDeleteSubList(g_lSettingsReqs, ind,ind); + + if(sToken == g_sSettingToken + "timeout") + g_fTimeOut = (float)sValue; + else if (sToken == g_sSettingToken + "verbose") + g_iVerbose = (integer)sValue; + else if (sToken == g_sGlobalToken+"devicename") + g_sDeviceName = sValue; + else if (sToken == g_sSettingToken+"autocouple") + g_iAutomate = (integer)sValue; + } else if(iNum == LM_SETTING_EMPTY){ + + //integer ind = llListFindList(g_lSettingsReqs, [sStr]); + //if(ind!=-1)g_lSettingsReqs = llDeleteSubList(g_lSettingsReqs, ind,ind); + + } else if(iNum == LM_SETTING_DELETE){ + + //integer ind = llListFindList(g_lSettingsReqs, [sStr]); + //if(ind!=-1)g_lSettingsReqs = llDeleteSubList(g_lSettingsReqs, ind,ind); + if(sStr==g_sSettingToken+"autocouple") g_iAutomate=FALSE; + } else if (iNum == DIALOG_RESPONSE) { + integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); + if (~iMenuIndex) { + list lMenuParams = llParseString2List(sStr, ["|"], []); + key kAv = (key)llList2String(lMenuParams, 0); + string sMessage = llList2String(lMenuParams, 1); + // integer iPage = (integer)llList2String(lMenuParams, 2); + integer iAuth = (integer)llList2String(lMenuParams, 3); + string sMenu=llList2String(g_lMenuIDs, iMenuIndex + 1); + g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex - 2 + g_iMenuStride); + if (sMenu == "couples") { + if (sMessage == UPMENU) + llMessageLinked(LINK_SET, iAuth, "menu " + g_sParentMenu, kAv); + else if (sMessage == STOP_COUPLES) { + StopAnims(); + CoupleAnimMenu(kAv, iAuth); + } else if (sMessage == TIME_COUPLES) { + string sPrompt = "\nChoose the duration for couple animations.\n\nCurrent duration: "; + if(g_fTimeOut == 0) sPrompt += "ENDLESS.\n\nNOTE: The non-looped \"pet\" sequence is an exception to this rule and can only play for 20 seconds at a time." ; + else sPrompt += "for "+(string)llCeil(g_fTimeOut)+" seconds."; + Dialog(kAv, sPrompt, ["10","20","30","40","60","90","120", "ENDLESS"], [UPMENU],0, iAuth,"timer"); + } else if(sMessage=="▣ Auto Acc.") + { + if (iAuth==CMD_OWNER || kAv==g_kWearer) + { + g_iAutomate=FALSE; + llMessageLinked(LINK_SET,LM_SETTING_DELETE,g_sSettingToken+"autocouple",""); + } + else + { + llMessageLinked(LINK_SET,NOTIFY,"0%NOACCESS% to changing this setting except by wearer or Owners.",kAv); + } + CoupleAnimMenu(kAv, iAuth); + } + else if(sMessage=="▢ Auto Acc.") + { + if (iAuth==CMD_OWNER || kAv==g_kWearer) + { + g_iAutomate=TRUE; + llMessageLinked(LINK_SET,LM_SETTING_SAVE,g_sSettingToken+"autocouple=1",""); + } + else + { + llMessageLinked(LINK_SET,NOTIFY,"0%NOACCESS% to changing this setting except by wearer or Owners.",kAv); + } + CoupleAnimMenu(kAv, iAuth); + } + else if (llGetSubString(sMessage,0,6) == "Verbose") { + if (llGetSubString(sMessage,8,-1) == "Off") { + g_iVerbose = FALSE; + llMessageLinked(LINK_SET, LM_SETTING_SAVE, g_sSettingToken + "verbose=" + (string)g_iVerbose, ""); + } else { + g_iVerbose = TRUE; + llMessageLinked(LINK_SET, LM_SETTING_DELETE, g_sSettingToken + "verbose", ""); + } + CoupleAnimMenu(kAv, iAuth); + } else { + integer iIndex = llListFindList(g_lAnimCmds, [sMessage]); + if (iIndex != -1) { + g_kCmdGiver = kAv; + g_iCmdAuth = iAuth; + g_iCmdIndex = iIndex; + //llSensor("", NULL_KEY, AGENT, g_fRange, PI); + Dialog(g_kCmdGiver, "\nChoose a partner:\n", [], ["BACK"], 0, iNum, "sensor"); + //g_kPart=llGenerateKey(); + //llMessageLinked(LINK_SET, SENSORDIALOG, (string)g_kCmdGiver + "|\nChoose a partner:\n|0|``"+(string)AGENT+"`"+(string)g_fRange+"`"+(string)PI + "|BACK|" + (string)iAuth, g_kPart); + } + } + } else if (sMenu == "sensor") { + //Debug("Response from partner"+sStr); + if (sMessage == UPMENU) CoupleAnimMenu(kAv, iAuth); + else { + g_kPartner = (key)sMessage; + g_sPartnerName = "secondlife:///app/agent/"+(string)g_kPartner+"/about"; + StopAnims(); + GetPartnerPermission(); + } + } else if (sMenu == "timer") { + //Debug("Response from timer menu"+sStr); + if (sMessage == UPMENU) CoupleAnimMenu(kAv, iAuth); + else if ((integer)sMessage > 0 && ((string)((integer)sMessage) == sMessage)) { + g_fTimeOut = (float)((integer)sMessage); + llMessageLinked(LINK_SET, LM_SETTING_SAVE, g_sSettingToken + "timeout=" + (string)g_fTimeOut, ""); + string sPet; + if (g_fTimeOut > 20.0) sPet = "(except the \"pet\" sequence) "; + llMessageLinked(LINK_SET,NOTIFY,"1"+"Couple Anmiations "+sPet+"play now for " + (string)llRound(g_fTimeOut) + " seconds.",kAv); + CoupleAnimMenu(kAv, iAuth); + } else if (sMessage == "ENDLESS") { + g_fTimeOut = 0.0; + llMessageLinked(LINK_SET, LM_SETTING_SAVE, g_sSettingToken + "timeout=0.0", ""); + llMessageLinked(LINK_SET,NOTIFY,"1"+"Couple Anmiations (except the \"pet\" sequence) play now forever. Use the menu or type \"/%CHANNEL% %PREFIX% stop couples\" to stop them again.",kAv); + CoupleAnimMenu(kAv, iAuth); + } + } + } + } else if (iNum == DIALOG_TIMEOUT) { + integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); + g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex +3); //remove stride from g_lMenuIDs + } else if (iNum == LOADPIN && sStr == llGetScriptName()) { + integer iPin = (integer)llFrand(99999.0)+1; + llSetRemoteScriptAccessPin(iPin); + llMessageLinked(iSender, LOADPIN, (string)iPin+"@"+llGetScriptName(),llGetKey()); + } + else if (iNum == REBOOT && sStr == "reboot") llResetScript(); + else if(iNum == LINK_CMD_DEBUG){ + integer onlyver=0; + if(sStr == "ver")onlyver=1; + llInstantMessage(kID, llGetScriptName() +" SCRIPT VERSION: "+g_sScriptVersion); + if(onlyver)return; // basically this command was: versions + DebugOutput(kID, [" PARTNER:", g_kPartner]); + } + } + not_at_target() { + llTargetRemove(g_iTargetID); + MoveToPartner(); + } + + at_target(integer tiNum, vector targetpos, vector ourpos) { + llTargetRemove(tiNum); + llStopMoveToTarget(); + float offset = 10.0; + if (g_iCmdIndex != -1) offset = (float)llList2String(g_lAnimSettings, g_iCmdIndex * 4 + 2); + list partnerDetails = llGetObjectDetails(g_kPartner, [OBJECT_POS, OBJECT_ROT]); + vector partnerPos = llList2Vector(partnerDetails, 0); + rotation partnerRot = llList2Rot(partnerDetails, 1); + vector myPos = llList2Vector(llGetObjectDetails(llGetOwner(), [OBJECT_POS]), 0); + + vector target = partnerPos + (<1.0, 0.0, 0.0> * partnerRot * offset); // target is meters in front of the partner + target.z = myPos.z; // ignore height differences + llMoveToTarget(target, g_fAlignTau); + llSleep(g_fAlignDelay); + llStopMoveToTarget(); + g_sSubAnim = llList2String(g_lAnimSettings, g_iCmdIndex * 4); + g_sDomAnim = llList2String(g_lAnimSettings, g_iCmdIndex * 4 + 1); + + llMessageLinked(LINK_SET, ANIM_START, g_sSubAnim, ""); + llRegionSayTo(g_kPartner,g_iLMChannel,(string)g_kPartner+"bootoff"); + if (anim_powered == "PARTNER") + { + integer iChannel = getChannel(g_kPartner); + llSay(iChannel, (string)g_kPartner + ":partnerpose "+g_sDomAnim); + } + else + { + llStartAnimation(g_sDomAnim); + } + g_iListener = llListen(g_iStopChan, "", g_kPartner, g_sStopString); + llMessageLinked(LINK_SET,NOTIFY,"0"+"If you would like to stop the animation early, say /" + (string)g_iStopChan + g_sStopString + " to stop.",g_kPartner); + + string sText = llList2String(g_lAnimSettings, g_iCmdIndex * 4 + 3); + if (sText != "" && g_iVerbose) { + sText = StrReplace(sText,"_PARTNER_",g_sPartnerName); + sText = StrReplace(sText,"_SELF_","%WEARERNAME%"); + llMessageLinked(LINK_SET,SAY,"0"+sText,""); + } + if (g_fTimeOut > 0.0) { + g_iAnimTimeout=llGetUnixTime()+(integer)g_fTimeOut; + if (g_sSubAnim == "~good" && g_fTimeOut > 20.0) g_iAnimTimeout = llGetUnixTime()+20; + } else if (g_sSubAnim == "~good") g_iAnimTimeout = llGetUnixTime()+20; + else g_iAnimTimeout=0; + refreshTimer(); + } + timer() + { + if (timer_reason == "PING") + { + //No Response, Fail + llSetTimerEvent(0.0); + timer_reason = ""; + ping_sent_to_key = NULL_KEY; + llMessageLinked(LINK_SET,NOTIFY,"0"+"Inviting "+ g_sPartnerName + " to a couples animation.",g_kWearer); + llMessageLinked(LINK_SET,NOTIFY,"0"+"%WEARERNAME% invited you to a couples animation! Click [Yes] to accept.",g_kPartner); + llRequestPermissions(g_kPartner, PERMISSION_TRIGGER_ANIMATION); + } + else + { + refreshTimer(); + } + } + dataserver(key kID, string sData) { + if (sData == EOF) iCardComplete++; + else { + list lParams = llParseString2List(sData, ["|"], []); + integer iLength = llGetListLength(lParams); + if (iLength == 4 || iLength == 5) { + if (!llGetInventoryType(llList2String(lParams, 1)) == INVENTORY_ANIMATION){ + llMessageLinked(LINK_SET,NOTIFY,"0"+CARD1 + " line " + (string)g_iLine1 + ": animation '" + llList2String(lParams, 1) + "' is not present. Skipping.",g_kWearer); + } else if (!llGetInventoryType(llList2String(lParams, 2)) == INVENTORY_ANIMATION){ + llMessageLinked(LINK_SET,NOTIFY,"0"+CARD1 + " line " + (string)g_iLine2 + ": animation '" + llList2String(lParams, 2) + "' is not present. Skipping.",g_kWearer); + } else { + integer iIndex = llListFindList(g_lAnimCmds, llList2List(lParams, 0, 0)); + if (~iIndex) { + g_lAnimCmds=llDeleteSubList(g_lAnimCmds,iIndex,iIndex); + g_lAnimSettings=llDeleteSubList(g_lAnimSettings,iIndex*4,iIndex*4+3); + } + g_lAnimCmds += llList2List(lParams, 0, 0); + g_lAnimSettings += llList2List(lParams, 1, 3); + g_lAnimSettings += [llList2String(lParams, 4)]; + //Debug(llDumpList2String(g_lAnimCmds, ",")); + //Debug(llDumpList2String(g_lAnimSettings, ",")); + } + } + if ( iCardComplete <2) { + if (kID == g_kDataID1) { + if (llGetInventoryType(CARD1) == INVENTORY_NOTECARD) { + g_iLine1++; + g_kDataID1 = llGetNotecardLine(CARD1, g_iLine1); + } + } else if (kID == g_kDataID2) { + if (llGetInventoryType(CARD2) == INVENTORY_NOTECARD) { + g_iLine2++; + g_kDataID2 = llGetNotecardLine(CARD2, g_iLine2); + } + } + } + } + } + + run_time_permissions(integer perm) { + if (perm & PERMISSION_TRIGGER_ANIMATION) { + key kID = llGetPermissionsKey(); + //Debug("changed anim permissions\nPerm ID="+(string)kID+"g_kPartner="+(string)g_kPartner); + if (kID == llGetOwner()) + { + integer iChannel = getChannel(g_kCmdGiver); + llSay(iChannel, (string)g_kCmdGiver + ":partnerpong"); + } + else if (kID == g_kPartner) { + g_iPermissionTimeout=0; + MoveToPartner(); + } else { + llMessageLinked(LINK_SET,NOTIFY,"1"+"Sorry, but the request timed out.",kID); + } + } + } + + changed(integer iChange) { + if (iChange & CHANGED_INVENTORY) { + if (llGetInventoryKey(CARD1) != g_kCardID1) StartNotecards(); + if (llGetInventoryKey(CARD2) != g_kCardID2) StartNotecards(); + } /* - if (iChange & CHANGED_REGION) { - if (g_iProfiled) { - llScriptProfiler(1); - Debug("profiling restarted"); - } - } + if (iChange & CHANGED_REGION) { + if (g_iProfiled) { + llScriptProfiler(1); + Debug("profiling restarted"); + } + } */ } } diff --git a/src/collar/oc_dialog.lsl b/src/collar/oc_dialog.lsl index b5806caf0..837e60244 100644 --- a/src/collar/oc_dialog.lsl +++ b/src/collar/oc_dialog.lsl @@ -12,6 +12,11 @@ Medea Destiny - version of function now works with prompts of any length and does not break up words. - Changed prompt for button descriptions in local chat from "Please check..." to "See..." to trigger it less often - Added warning text to prompt when prompt is trunctated, so menu prompt text will now show "(CONT. IN LOCAL CHAT)" + Mar 2024 - Emergency fix for extended sensor function flooding memory and crashing script + +Nikki Lacrima + Aug 2023 - Changed functions for clearing auth so auth doesn't persist for open menus + */ integer CMD_ZERO = 0; integer CMD_OWNER = 500; @@ -566,6 +571,9 @@ state active sensor(integer num_detected){ + if(num_detected>16) num_detected=16; + //LL just expanded sensor to up to 32 hits. Thanks LL, but there's a ton of content + //out there not designed to cope with that which are gonna stack-heap now. For now at least we'll trim back to 16. //get sensot request info from list list lParams=llParseStringKeepNulls(llList2String(g_lSensorDetails,0), ["|"], []); key kID = (key)llList2String(g_lSensorDetails,1); @@ -695,9 +703,17 @@ state active } else if (sToken == g_sGlobalToken+"prefix"){ if (sValue != "") g_sPrefix=sValue; } else if (sToken == g_sGlobalToken+"channel") g_iListenChan = (integer)sValue; - else if (sToken == "auth_owner") - g_lOwners = llParseString2List(sValue, [","], []); - else if(sToken == g_sGlobalToken + "showlevel") g_iShowLevel = (integer)sValue; + else if (sToken == "auth_owner") { + list t_lOwners = llParseString2List(sValue, [","], []); + integer iPos =0; + integer iEnd = llGetListLength(g_lOwners); + for(iPos=0;iPosCMD_EVERYONE) return R(); - - - - llListenRemove(g_iTmpLstn); - g_iTmpLstnChn = llRound(llFrand(438888)); - g_iTmpLstn = llListen(g_iTmpLstnChn, "", g_kWearer, ""); - - llOwnerSay("@getinvworn:"+g_sPath+"="+(string)g_iTmpLstnChn); - - llResetTime(); - llSetTimerEvent(1); -} -integer g_iFindChn; -integer g_iFindLstn = -1; -integer g_iCmdMode = 0; -integer F_REMOVE = 1; -integer F_RECURSIVE = 2; -integer F_ADD = 4; -integer F_WEAR = 8; -key g_kChatUser; // user of a chat command, to notify if nothing found. - -UserCommand(integer iNum, string sStr, key kID) { - - - - if (iNumCMD_EVERYONE) return; - if (iNum == CMD_OWNER && llToLower(sStr) == "runaway") { - g_lOwner=[]; - g_lTrust=[]; - g_lBlock=[]; - return; - } - if (llToLower(sStr)==llToLower(g_sSubMenu) || llToLower(sStr) == "menu "+llToLower(g_sSubMenu)) Menu(kID, iNum); - else if(llToLower(sStr) == "folders" || llToLower(sStr) == "menu folders")Menu(kID, iNum); - //else if (iNum!=CMD_OWNER && iNum!=CMD_TRUSTED && kID!=g_kWearer) RelayNotify(kID,"Access denied!",0); - else { - //integer iWSuccess = 0; - string sChangetype = llGetSubString(sStr,0,0); - if(llListFindList(["&","-","+"],[sChangetype])==-1) return; - sChangetype = llGetSubString(sStr, 0, 1); - string sChangevalue = llStringTrim(llDeleteSubString(sStr, 0, 1), STRING_TRIM); - //string sText; - if(sChangevalue==""){ // don't search for empty strings. - llMessageLinked(LINK_SET, NOTIFY, "0You have to supply a string to search for to use add/remove/wear commands.", kID); - return; - } - g_kChatUser=kID; - if(iNum == CMD_TRUSTED && !Bool((g_iAccessBitSet&1)))return R(); - if(iNum == CMD_EVERYONE && !Bool((g_iAccessBitSet&2)))return R(); - if(iNum == CMD_GROUP && !Bool((g_iAccessBitSet&4)))return R(); - if(iNum == CMD_WEARER && !Bool((g_iAccessBitSet&8)))return R(); - if(g_iFindLstn != -1)llListenRemove(g_iFindLstn); - - g_iFindChn = llRound(llFrand(99999999)); - g_iFindLstn =llListen(g_iFindChn, "", llGetOwner(), ""); - - if(sChangetype == "--"){ - //llOwnerSay("@detachall:"+sChangevalue+"=force"); - g_iCmdMode=F_REMOVE | F_RECURSIVE; - llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); - return; - } else if(sChangetype == "&&"){ - g_iCmdMode = F_ADD | F_RECURSIVE; - //llOwnerSay("@attachallover:"+sChangevalue+"=force"); - llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); - return; - } else if(sChangetype == "++"){ - g_iCmdMode = F_ADD; - //llOwnerSay("@attachall:"+sChangevalue+"=force"); - llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); - return; - } - - sChangetype = llGetSubString(sStr,0,0); - sChangevalue = llStringTrim(llDeleteSubString(sStr, 0, 0), STRING_TRIM); - if(sChangevalue==""){ // don't search for empty strings. - llMessageLinked(LINK_SET, NOTIFY, "0You have to supply a string to search for to use add/remove/wear commands.", kID); - return; - } - if(sChangetype == "&"){ - // add folder path - //llOwnerSay("@attachover:"+sChangevalue+"=force"); - g_iCmdMode = F_ADD; - llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); - return; - } else if(sChangetype == "-"){ - //llOwnerSay("@detach:"+sChangevalue+"=force"); - g_iCmdMode = F_REMOVE; - llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); - return; - } else if(sChangetype == "+"){ - //llOwnerSay("@attach:"+sChangevalue+"=force"); - g_iCmdMode=F_WEAR; - llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); - return; - } - llListenRemove(g_iFindLstn); - g_iFindLstn = -1; - } -} - -//integer TIMEOUT_READY = 30497; -//integer TIMEOUT_REGISTER = 30498; -//integer TIMEOUT_FIRED = 30499; - - -key g_kWearer; -list g_lMenuIDs; -integer g_iMenuStride; -list g_lOwner; -list g_lTrust; -list g_lBlock; -integer g_iLocked=FALSE; - - -list g_lFolderCheckboxes = ["▢", "▣", "◑"]; -string Checkbox(integer iChecked, string sLabel){ - if(iChecked>3 || iChecked<0)iChecked=0; - return llList2String(g_lFolderCheckboxes, iChecked)+" "+sLabel; -} - - -///The setor method is derived from a similar PHP proposed function, though it was denied, -///https://wiki.php.net/rfc/ifsetor -///The concept is roughly the same though we're not dealing with lists in this method, so is just modified -///The ifsetor proposal would give a function which would be more like -///ifsetor(list[index], sTrue, sFalse) -///LSL can't check if a list item is set without a stack heap if it is out of range, this is significantly easier for us to just check for a integer boolean -string setor(integer iTest, string sTrue, string sFalse){ - if(iTest)return sTrue; - else return sFalse; -} - - - -integer ALIVE = -55; -integer READY = -56; -integer STARTUP = -57; - -integer g_iMenuAuth; - -LocksMenu(key kAv, integer iAuth, integer iMask) -{ - string sPrompt = "[Folders]\n> Locks\n\nPath: "+g_sPath+"\n\n* The subfolder options will lock (check) / unlock (uncheck) this folder and all subfolders contained. It only affects your current path. To unlock every folder and clear all locks, see the main menu"; - list lButtons = []; - if(iMask & 1)lButtons += [Checkbox(TRUE, "det. subfolder")]; - else lButtons += Checkbox(FALSE, "det. subfolder"); - if(iMask & 2)lButtons += [Checkbox(TRUE, "att. subfolder")]; - else lButtons += Checkbox(FALSE, "att. subfolder"); - if(iMask & 4)lButtons += [Checkbox(TRUE, "det. this")]; - else lButtons += [Checkbox(FALSE, "det. this")]; - if(iMask & 8)lButtons += [Checkbox(TRUE, "att. this")]; - else lButtons += [Checkbox(FALSE, "att. this")]; - - Dialog(kAv, sPrompt, lButtons, [UPMENU], 0, iAuth, "Folders~Locks"); -} -default -{ - on_rez(integer iNum){ - llResetScript(); - } - state_entry(){ - llMessageLinked(LINK_SET, ALIVE, llGetScriptName(),""); - } - link_message(integer iSender, integer iNum, string sStr, key kID){ - if(iNum == REBOOT){ - if(sStr == "reboot"){ - llResetScript(); - } - } else if(iNum == READY){ - llMessageLinked(LINK_SET, ALIVE, llGetScriptName(), ""); - } else if(iNum == STARTUP){ - state active; - } - } -} -state active -{ - on_rez(integer t){ - llResetScript(); - } - state_entry() - { - g_kWearer = llGetOwner(); - } - timer(){ - if(llGetTime()>=60.0){ - //llSay(0, "menu has timed out"); - llListenRemove(g_iTmpLstn); - llSetTimerEvent(0); - } - } - listen(integer iChan, string sName, key kID, string sMsg){ - if(iChan == g_iTmpLstnChn){ - /* - 0 : No item is present in that folder - 1 : Some items are present in that folder, but none of them is worn - 2 : Some items are present in that folder, and some of them are worn - 3 : Some items are present in that folder, and all of them are worn - */ - - list lFolders = llParseString2List(sMsg, [","],[]); - list lButtons = []; - - list lTmp1 = llParseStringKeepNulls(llList2String(lFolders,0),["|"],[]); - integer iSub1 = (integer)llGetSubString(llList2String(lTmp1,1),0,0); - integer iSub2 = (integer)llGetSubString(llList2String(lTmp1,1),1,1); - integer iState; - if(iSub1 == 3 || iSub2 == 3) iState=1; - else if(iSub1 == 2 || iSub2 == 2)iState = 2; - else if(iSub1 == 1 || iSub2 == 1)iState=0; - // Build menu prompt - string sPrompt = "\n[Folder Browser]\n\nLegend:\n"; - sPrompt += llList2String(g_lFolderCheckboxes,0) + " = Nothing in the folder is worn, or any subfolders\n"; - sPrompt += llList2String(g_lFolderCheckboxes,1) + " = All items in either this folder or its subfolder are worn\n"; - sPrompt += llList2String(g_lFolderCheckboxes,2) + " = Some items are worn in this folder, or its subfolders\n"; - // show current folder with state indicator - sPrompt += "\nCurrently browsing path: "+Checkbox(iState, setor(g_sPath == "", "#RLV", g_sPath))+"\n"; - // buttons for other folders - integer i; - integer len = llGetListLength(lFolders); - for(i=1;i= CMD_OWNER && iNum <= CMD_EVERYONE) UserCommand(iNum, sStr, kID); - else if(iNum == MENUNAME_REQUEST && sStr == g_sParentMenu) - llMessageLinked(iSender, MENUNAME_RESPONSE, g_sParentMenu+"|"+ g_sSubMenu,""); - else if(iNum == DIALOG_RESPONSE){ - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - if(iMenuIndex!=-1){ - string sMenu = llList2String(g_lMenuIDs, iMenuIndex+1); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex-1, iMenuIndex-2+g_iMenuStride); - list lMenuParams = llParseString2List(sStr, ["|"],[]); - key kAv = llList2Key(lMenuParams,0); - string sMsg = llList2String(lMenuParams,1); - integer iAuth = llList2Integer(lMenuParams,3); - integer iRespring=TRUE; - - - if(sMenu == "FolderBrowser~"){ - if(sMsg == UPMENU) { - iRespring=FALSE; - Menu(kAv,iAuth); - } else if(sMsg == "+ Add Items"){ - llOwnerSay("@attachallover:"+g_sPath+"=force"); - llSleep(2.0); - } else if(sMsg == "- Rem Items"){ - llOwnerSay("@detachall:"+g_sPath+"=force"); - llSleep(2.0); - } else if(sMsg == "Locks..") - { - iRespring=FALSE; - //LocksMenu(kAv, iAuth); - g_kMenuUser=kAv; - g_iMenuAuth = iAuth; - llMessageLinked(LINK_SET, QUERY_FOLDER_LOCKS, g_sPath, ""); - } else if(sMsg == "^ UP"){ - iRespring=FALSE; - Browser(kAv,iAuth, GoBackOneFolder(g_sPath)); - } else { - list lTmpBtn = llParseString2List(sMsg, [" "], []); - string TheButton = llDumpList2String(llList2List(lTmpBtn,1,-1), " "); - Browser(kAv,iAuth, MakePath(g_sPath,TheButton)); - iRespring=FALSE; - } - - - if(iRespring)Browser(kAv,iAuth, g_sPath); - } else if(sMenu == "Folders~Locks"){ - if(sMsg == UPMENU){ - iRespring=FALSE; - Browser(kAv,iAuth, g_sPath); - }else if(sMsg == Checkbox(TRUE, "det. this") || sMsg==Checkbox(FALSE, "det. this")){ - llMessageLinked(LINK_SET, SET_FOLDER_LOCK, "detachthis", g_sPath); - } else if(sMsg == Checkbox(TRUE, "att. this") || sMsg == Checkbox(FALSE, "att. this")) - { - llMessageLinked(LINK_SET, SET_FOLDER_LOCK, "attachthis", g_sPath); - } else if(sMsg == Checkbox(TRUE, "det. subfolder") || sMsg == Checkbox(FALSE, "det. subfolder")){ - llMessageLinked(LINK_SET, SET_FOLDER_LOCK, "detachallthis", g_sPath); - } else if(sMsg == Checkbox(TRUE, "att. subfolder") || sMsg == Checkbox(FALSE, "att. subfolder")){ - llMessageLinked(LINK_SET, SET_FOLDER_LOCK, "attachallthis", g_sPath); - } - - if(iRespring)llMessageLinked(LINK_SET, QUERY_FOLDER_LOCKS, g_sPath, ""); - } else if(sMenu == "Menu~Folders"){ - if(sMsg == "Browse"){ - Browser(kAv,iAuth,""); - iRespring=FALSE; - } else if(sMsg == CONFIG){ - ConfigureMenu(kAv,iAuth); - iRespring=FALSE; - } else if(sMsg == "Clear Locks"){ - llMessageLinked(LINK_SET, CLEAR_FOLDER_LOCKS, "", ""); - } else if(sMsg == UPMENU){ - iRespring=FALSE; - - llMessageLinked(LINK_SET, iAuth, "menu "+g_sParentMenu, kAv); - } - - - if(iRespring)Menu(kAv,iAuth); - } else if(sMenu == "Folders~Configure"){ - - // configuration menu - if(sMsg==UPMENU){ - iRespring=FALSE; - Menu(kAv,iAuth); - } else { - - list ButtonFlags = llParseString2List(sMsg,[" "],[]); - string ButtonLabel = llDumpList2String(llList2List(ButtonFlags,1,-1), " "); - integer Enabled = llListFindList(g_lFolderCheckboxes, [llList2String(ButtonFlags,0)]); - if(ButtonLabel=="Hide ~") - { - g_iHideTilde=!g_iHideTilde; - llMessageLinked(LINK_SET, LM_SETTING_SAVE, "folders_hidetilde="+(string)g_iHideTilde,""); - } - else if(Enabled){ - // Disable flag - if(ButtonLabel == "Trusted")g_iAccessBitSet -=1; - else if(ButtonLabel == "Public")g_iAccessBitSet-=2; - else if(ButtonLabel == "Group")g_iAccessBitSet-=4; - else if(ButtonLabel == "Wearer")g_iAccessBitSet-=8; - }else{ - if(ButtonLabel == "Trusted")g_iAccessBitSet+=1; - else if(ButtonLabel == "Public")g_iAccessBitSet+=2; - else if(ButtonLabel == "Group")g_iAccessBitSet+=4; - else if(ButtonLabel == "Wearer") g_iAccessBitSet+=8; - } - - // save - llMessageLinked(LINK_SET, LM_SETTING_SAVE, "folders_accessflags="+(string)g_iAccessBitSet, ""); - } - - if(iRespring) ConfigureMenu(kAv,iAuth); - } - } - } else if (iNum == DIALOG_TIMEOUT) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex +3); //remove stride from g_lMenuIDs - } else if(iNum == LM_SETTING_RESPONSE){ - // Detect here the Settings - list lSettings = llParseString2List(sStr, ["_","="],[]); - - //integer ind = llListFindList(g_lSettingsReqs, [llList2String(lSettings,0)+"_"+llList2String(lSettings,1)]); - // if(ind!=-1)g_lSettingsReqs = llDeleteSubList(g_lSettingsReqs, ind,ind); - - - if(llList2String(lSettings,0)=="global"){ - if(llList2String(lSettings,1)=="locked"){ - g_iLocked=llList2Integer(lSettings,2); - } else if(llList2String(lSettings,1) == "checkboxes"){ - g_lFolderCheckboxes = llParseString2List(llList2String(lSettings,2),[","],[])+["◑"]; - } - } else if(llList2String(lSettings,0)=="folders"){ - if(llList2String(lSettings,1) == "accessflags"){ - g_iAccessBitSet=(integer)llList2String(lSettings,2); - } else if(llList2String(lSettings,1) =="hidetilde"){ - g_iHideTilde=(integer)llList2String(lSettings,2); - } - - } - } else if(iNum == REPLY_FOLDER_LOCKS) - { - integer iMask = (integer)((string)kID); - LocksMenu(g_kMenuUser, g_iMenuAuth, iMask); - } - //llOwnerSay(llDumpList2String([iSender,iNum,sStr,kID],"^")); - } -} +/* +This file is a part of OpenCollar. +Copyright 2020 +: Contributors : +Aria (Tashia Redrose) + * Aug 2020 - Rewrote oc_folders for 8.0 Alpha 5 +Medea (Medea Destiny) + * June 2021 - *Fix issue #570, Allow hiding folders starting with ~ via HideTilde option, defaults to ON. + *Fix issue #581, filtering input to UserCommand to only folder-auth check actual folder + commands rather than folder-authing and processing EVERYTHING THE COLLAR DOES BY ANY USER + * May 2022 - *Fix issue #775, changed string handling to ensure command is not issued as part of search + term, added sanity checking for chat commands to ensure we don't try to search for nothing, + and no longer operate on empty search results. Issuer of chat command now stored as + g_kChatUser so they can be notified if findfolder fails. + +Neil2Ball & Sue Cripter - Fix for OpenSim, replacing invalid return r(); with {r(); return} + +et al. +Licensed under the GPLv2. See LICENSE for full details. +https://github.com/OpenCollarTeam/OpenCollar +*/ + + +string g_sParentMenu = "RLV"; +string g_sSubMenu = "# Folders"; + + +//MESSAGE MAP +//integer CMD_ZERO = 0; +integer CMD_OWNER = 500; +integer CMD_TRUSTED = 501; +integer CMD_GROUP = 502; +integer CMD_WEARER = 503; +integer CMD_EVERYONE = 504; +//integer CMD_RLV_RELAY = 507; +//integer CMD_SAFEWORD = 510; +//integer CMD_RELAY_SAFEWORD = 511; + +integer NOTIFY = 1002; +integer REBOOT = -1000; + +integer LM_SETTING_SAVE = 2000;//scripts send messages on this channel to have settings saved +//str must be in form of "token=value" +//integer LM_SETTING_REQUEST = 2001;//when startup, scripts send requests for settings on this channel +integer LM_SETTING_RESPONSE = 2002;//the settings script sends responses on this channel +//integer LM_SETTING_DELETE = 2003;//delete token from settings +//integer LM_SETTING_EMPTY = 2004;//sent when a token has no value + +integer MENUNAME_REQUEST = 3000; +integer MENUNAME_RESPONSE = 3001; +//integer MENUNAME_REMOVE = 3003; + +//integer RLV_CMD = 6000; +//integer RLV_REFRESH = 6001;//RLV plugins should reinstate their restrictions upon receiving this message. + +//integer RLV_OFF = 6100; // send to inform plugins that RLV is disabled now, no message or key needed +//integer RLV_ON = 6101; // send to inform plugins that RLV is enabled now, no message or key needed + +integer QUERY_FOLDER_LOCKS = -9100; +integer REPLY_FOLDER_LOCKS = -9101; +integer SET_FOLDER_LOCK = -9102; +integer CLEAR_FOLDER_LOCKS = -9103; + + +integer DIALOG = -9000; +integer DIALOG_RESPONSE = -9001; +integer DIALOG_TIMEOUT = -9002; +string UPMENU = "BACK"; +//string ALL = "ALL"; + +Dialog(key kID, string sPrompt, list lChoices, list lUtilityButtons, integer iPage, integer iAuth, string sName) { + key kMenuID = llGenerateKey(); + llMessageLinked(LINK_SET, DIALOG, (string)kID + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lChoices, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, kMenuID); + + integer iIndex = llListFindList(g_lMenuIDs, [kID]); + if (~iIndex) g_lMenuIDs = llListReplaceList(g_lMenuIDs, [kID, kMenuID, sName], iIndex, iIndex + g_iMenuStride - 1); + else g_lMenuIDs += [kID, kMenuID, sName]; +} + + +integer g_iHideTilde=TRUE; + +integer g_iTmpLstnChn; +integer g_iTmpLstn; +string CONFIG = "◌ Configure"; +Menu(key kID, integer iAuth) { + string sPrompt = "\n[Folders]"; + list lButtons = ["Browse", CONFIG, "Clear Locks"]; + + Dialog(kID,sPrompt,lButtons, [UPMENU], 0, iAuth, "Menu~Folders"); + //Browser(kID, iAuth, ""); +} + +integer g_iAccessBitSet=11; + +string TrueOrFalse(integer iTest){ + if(iTest)return "true"; + else return "false"; +} + +/// LSL bools can be larger than 1 but will always return TRUE if not 0, slam it to a 1 or a 0 for simplicity, and to save on memory. +integer Bool(integer iTest){ + if(iTest)return TRUE; + else return FALSE; +} + +ConfigureMenu(key kID, integer iAuth){ + if(iAuth == CMD_OWNER){ + // allow configuration of access levels only to owner + string sPrompt = "\n[Folders Configuration]\n\n"; + + integer iTrusted = Bool((g_iAccessBitSet&1)); + integer iPublic = Bool((g_iAccessBitSet&2)); + integer iGroup = Bool((g_iAccessBitSet&4)); + integer iWearer = Bool((g_iAccessBitSet&8)); + string sTrusted = TrueOrFalse(iTrusted); + string sPublic = TrueOrFalse(iPublic); + string sGroup = TrueOrFalse(iGroup); + string sWearer = TrueOrFalse(iWearer); + Dialog(kID, sPrompt+"\n * Hide folders that start with '~': "+TrueOrFalse(g_iHideTilde)+"\n * Owner: ALWAYS\n * Trusted: "+sTrusted+"\n * Public: "+sPublic+"\n * Group: "+sGroup+"\n * Wearer: "+sWearer+"\n", [Checkbox(g_iHideTilde,"Hide ~"),Checkbox(iTrusted, "Trusted"), Checkbox(iPublic, "Public") ,Checkbox(iGroup, "Group"), Checkbox(iWearer, "Wearer")], [UPMENU], 0, iAuth, "Folders~Configure"); + + + }else { + llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to folders configuration", kID); + Menu(kID,iAuth); + } +} + + + +string GoBackOneFolder(string sPath){ + list lTmp = llParseStringKeepNulls(sPath, ["/"],[]); + lTmp=llDeleteSubList(lTmp,-1,-1); + return llDumpList2String(lTmp,"/"); +} + +string MakePath(string sCurrent, string sNext){ + list lTmp = llParseStringKeepNulls(sCurrent, ["/"],[]); + lTmp += sNext; + return llDumpList2String(lTmp,"/"); +} + + +key g_kMenuUser; +integer g_iMenuUser; +string g_sPath; + +R(){ + llMessageLinked(LINK_SET, NOTIFY, "0%NOACCESS% to browsing #RLV folders", g_kMenuUser); + Menu(g_kMenuUser, g_iMenuUser); +} +Browser(key kID, integer iAuth, string sPath){ + + // Check auth real fast, then give menu, or kick back to main menu + + g_kMenuUser=kID; + g_iMenuUser=iAuth; + g_sPath = sPath; + + if(iAuth == CMD_TRUSTED && !Bool((g_iAccessBitSet&1))){ R(); return; } + if(iAuth == CMD_EVERYONE && !Bool((g_iAccessBitSet&2))){ R(); return; } + if(iAuth == CMD_GROUP && !Bool((g_iAccessBitSet&4))){ R(); return; } + if(iAuth == CMD_WEARER && !Bool((g_iAccessBitSet&8))){ R(); return; } + if (iAuthCMD_EVERYONE) { R(); return; } + + + + llListenRemove(g_iTmpLstn); + g_iTmpLstnChn = llRound(llFrand(438888)); + g_iTmpLstn = llListen(g_iTmpLstnChn, "", g_kWearer, ""); + + llOwnerSay("@getinvworn:"+g_sPath+"="+(string)g_iTmpLstnChn); + + llResetTime(); + llSetTimerEvent(1); +} +integer g_iFindChn; +integer g_iFindLstn = -1; +integer g_iCmdMode = 0; +integer F_REMOVE = 1; +integer F_RECURSIVE = 2; +integer F_ADD = 4; +integer F_WEAR = 8; +key g_kChatUser; // user of a chat command, to notify if nothing found. + +UserCommand(integer iNum, string sStr, key kID) { + + + + if (iNumCMD_EVERYONE) return; + if (iNum == CMD_OWNER && llToLower(sStr) == "runaway") { + g_lOwner=[]; + g_lTrust=[]; + g_lBlock=[]; + return; + } + if (llToLower(sStr)==llToLower(g_sSubMenu) || llToLower(sStr) == "menu "+llToLower(g_sSubMenu)) Menu(kID, iNum); + else if(llToLower(sStr) == "folders" || llToLower(sStr) == "menu folders")Menu(kID, iNum); + //else if (iNum!=CMD_OWNER && iNum!=CMD_TRUSTED && kID!=g_kWearer) RelayNotify(kID,"Access denied!",0); + else { + //integer iWSuccess = 0; + string sChangetype = llGetSubString(sStr,0,0); + if(llListFindList(["&","-","+"],[sChangetype])==-1) return; + sChangetype = llGetSubString(sStr, 0, 1); + string sChangevalue = llStringTrim(llDeleteSubString(sStr, 0, 1), STRING_TRIM); + //string sText; + if(sChangevalue==""){ // don't search for empty strings. + llMessageLinked(LINK_SET, NOTIFY, "0You have to supply a string to search for to use add/remove/wear commands.", kID); + return; + } + g_kChatUser=kID; + if(iNum == CMD_TRUSTED && !Bool((g_iAccessBitSet&1))){ R(); return; } + if(iNum == CMD_EVERYONE && !Bool((g_iAccessBitSet&2))){ R(); return; } + if(iNum == CMD_GROUP && !Bool((g_iAccessBitSet&4))){ R(); return; } + if(iNum == CMD_WEARER && !Bool((g_iAccessBitSet&8))){ R(); return; } + if(g_iFindLstn != -1)llListenRemove(g_iFindLstn); + + g_iFindChn = llRound(llFrand(99999999)); + g_iFindLstn =llListen(g_iFindChn, "", llGetOwner(), ""); + + if(sChangetype == "--"){ + //llOwnerSay("@detachall:"+sChangevalue+"=force"); + g_iCmdMode=F_REMOVE | F_RECURSIVE; + llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); + return; + } else if(sChangetype == "&&"){ + g_iCmdMode = F_ADD | F_RECURSIVE; + //llOwnerSay("@attachallover:"+sChangevalue+"=force"); + llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); + return; + } else if(sChangetype == "++"){ + g_iCmdMode = F_ADD; + //llOwnerSay("@attachall:"+sChangevalue+"=force"); + llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); + return; + } + + sChangetype = llGetSubString(sStr,0,0); + sChangevalue = llStringTrim(llDeleteSubString(sStr, 0, 0), STRING_TRIM); + if(sChangevalue==""){ // don't search for empty strings. + llMessageLinked(LINK_SET, NOTIFY, "0You have to supply a string to search for to use add/remove/wear commands.", kID); + return; + } + if(sChangetype == "&"){ + // add folder path + //llOwnerSay("@attachover:"+sChangevalue+"=force"); + g_iCmdMode = F_ADD; + llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); + return; + } else if(sChangetype == "-"){ + //llOwnerSay("@detach:"+sChangevalue+"=force"); + g_iCmdMode = F_REMOVE; + llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); + return; + } else if(sChangetype == "+"){ + //llOwnerSay("@attach:"+sChangevalue+"=force"); + g_iCmdMode=F_WEAR; + llOwnerSay("@findfolder:"+sChangevalue+"="+(string)g_iFindChn); + return; + } + llListenRemove(g_iFindLstn); + g_iFindLstn = -1; + } +} + +//integer TIMEOUT_READY = 30497; +//integer TIMEOUT_REGISTER = 30498; +//integer TIMEOUT_FIRED = 30499; + + +key g_kWearer; +list g_lMenuIDs; +integer g_iMenuStride; +list g_lOwner; +list g_lTrust; +list g_lBlock; +integer g_iLocked=FALSE; + + +list g_lFolderCheckboxes = ["▢", "▣", "◑"]; +string Checkbox(integer iChecked, string sLabel){ + if(iChecked>3 || iChecked<0)iChecked=0; + return llList2String(g_lFolderCheckboxes, iChecked)+" "+sLabel; +} + + +///The setor method is derived from a similar PHP proposed function, though it was denied, +///https://wiki.php.net/rfc/ifsetor +///The concept is roughly the same though we're not dealing with lists in this method, so is just modified +///The ifsetor proposal would give a function which would be more like +///ifsetor(list[index], sTrue, sFalse) +///LSL can't check if a list item is set without a stack heap if it is out of range, this is significantly easier for us to just check for a integer boolean +string setor(integer iTest, string sTrue, string sFalse){ + if(iTest)return sTrue; + else return sFalse; +} + + + +integer ALIVE = -55; +integer READY = -56; +integer STARTUP = -57; + +integer g_iMenuAuth; + +LocksMenu(key kAv, integer iAuth, integer iMask) +{ + string sPrompt = "[Folders]\n> Locks\n\nPath: "+g_sPath+"\n\n* The subfolder options will lock (check) / unlock (uncheck) this folder and all subfolders contained. It only affects your current path. To unlock every folder and clear all locks, see the main menu"; + list lButtons = []; + if(iMask & 1)lButtons += [Checkbox(TRUE, "det. subfolder")]; + else lButtons += Checkbox(FALSE, "det. subfolder"); + if(iMask & 2)lButtons += [Checkbox(TRUE, "att. subfolder")]; + else lButtons += Checkbox(FALSE, "att. subfolder"); + if(iMask & 4)lButtons += [Checkbox(TRUE, "det. this")]; + else lButtons += [Checkbox(FALSE, "det. this")]; + if(iMask & 8)lButtons += [Checkbox(TRUE, "att. this")]; + else lButtons += [Checkbox(FALSE, "att. this")]; + + Dialog(kAv, sPrompt, lButtons, [UPMENU], 0, iAuth, "Folders~Locks"); +} +default +{ + on_rez(integer iNum){ + llResetScript(); + } + state_entry(){ + llMessageLinked(LINK_SET, ALIVE, llGetScriptName(),""); + } + link_message(integer iSender, integer iNum, string sStr, key kID){ + if(iNum == REBOOT){ + if(sStr == "reboot"){ + llResetScript(); + } + } else if(iNum == READY){ + llMessageLinked(LINK_SET, ALIVE, llGetScriptName(), ""); + } else if(iNum == STARTUP){ + state active; + } + } +} +state active +{ + on_rez(integer t){ + llResetScript(); + } + state_entry() + { + g_kWearer = llGetOwner(); + } + timer(){ + if(llGetTime()>=60.0){ + //llSay(0, "menu has timed out"); + llListenRemove(g_iTmpLstn); + llSetTimerEvent(0); + } + } + listen(integer iChan, string sName, key kID, string sMsg){ + if(iChan == g_iTmpLstnChn){ + /* + 0 : No item is present in that folder + 1 : Some items are present in that folder, but none of them is worn + 2 : Some items are present in that folder, and some of them are worn + 3 : Some items are present in that folder, and all of them are worn + */ + + list lFolders = llParseString2List(sMsg, [","],[]); + list lButtons = []; + + list lTmp1 = llParseStringKeepNulls(llList2String(lFolders,0),["|"],[]); + integer iSub1 = (integer)llGetSubString(llList2String(lTmp1,1),0,0); + integer iSub2 = (integer)llGetSubString(llList2String(lTmp1,1),1,1); + integer iState; + if(iSub1 == 3 || iSub2 == 3) iState=1; + else if(iSub1 == 2 || iSub2 == 2)iState = 2; + else if(iSub1 == 1 || iSub2 == 1)iState=0; + // Build menu prompt + string sPrompt = "\n[Folder Browser]\n\nLegend:\n"; + sPrompt += llList2String(g_lFolderCheckboxes,0) + " = Nothing in the folder is worn, or any subfolders\n"; + sPrompt += llList2String(g_lFolderCheckboxes,1) + " = All items in either this folder or its subfolder are worn\n"; + sPrompt += llList2String(g_lFolderCheckboxes,2) + " = Some items are worn in this folder, or its subfolders\n"; + // show current folder with state indicator + sPrompt += "\nCurrently browsing path: "+Checkbox(iState, setor(g_sPath == "", "#RLV", g_sPath))+"\n"; + // buttons for other folders + integer i; + integer len = llGetListLength(lFolders); + for(i=1;i= CMD_OWNER && iNum <= CMD_EVERYONE) UserCommand(iNum, sStr, kID); + else if(iNum == MENUNAME_REQUEST && sStr == g_sParentMenu) + llMessageLinked(iSender, MENUNAME_RESPONSE, g_sParentMenu+"|"+ g_sSubMenu,""); + else if(iNum == DIALOG_RESPONSE){ + integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); + if(iMenuIndex!=-1){ + string sMenu = llList2String(g_lMenuIDs, iMenuIndex+1); + g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex-1, iMenuIndex-2+g_iMenuStride); + list lMenuParams = llParseString2List(sStr, ["|"],[]); + key kAv = llList2Key(lMenuParams,0); + string sMsg = llList2String(lMenuParams,1); + integer iAuth = llList2Integer(lMenuParams,3); + integer iRespring=TRUE; + + + if(sMenu == "FolderBrowser~"){ + if(sMsg == UPMENU) { + iRespring=FALSE; + Menu(kAv,iAuth); + } else if(sMsg == "+ Add Items"){ + llOwnerSay("@attachallover:"+g_sPath+"=force"); + llSleep(2.0); + } else if(sMsg == "- Rem Items"){ + llOwnerSay("@detachall:"+g_sPath+"=force"); + llSleep(2.0); + } else if(sMsg == "Locks..") + { + iRespring=FALSE; + //LocksMenu(kAv, iAuth); + g_kMenuUser=kAv; + g_iMenuAuth = iAuth; + llMessageLinked(LINK_SET, QUERY_FOLDER_LOCKS, g_sPath, ""); + } else if(sMsg == "^ UP"){ + iRespring=FALSE; + Browser(kAv,iAuth, GoBackOneFolder(g_sPath)); + } else { + list lTmpBtn = llParseString2List(sMsg, [" "], []); + string TheButton = llDumpList2String(llList2List(lTmpBtn,1,-1), " "); + Browser(kAv,iAuth, MakePath(g_sPath,TheButton)); + iRespring=FALSE; + } + + + if(iRespring)Browser(kAv,iAuth, g_sPath); + } else if(sMenu == "Folders~Locks"){ + if(sMsg == UPMENU){ + iRespring=FALSE; + Browser(kAv,iAuth, g_sPath); + }else if(sMsg == Checkbox(TRUE, "det. this") || sMsg==Checkbox(FALSE, "det. this")){ + llMessageLinked(LINK_SET, SET_FOLDER_LOCK, "detachthis", g_sPath); + } else if(sMsg == Checkbox(TRUE, "att. this") || sMsg == Checkbox(FALSE, "att. this")) + { + llMessageLinked(LINK_SET, SET_FOLDER_LOCK, "attachthis", g_sPath); + } else if(sMsg == Checkbox(TRUE, "det. subfolder") || sMsg == Checkbox(FALSE, "det. subfolder")){ + llMessageLinked(LINK_SET, SET_FOLDER_LOCK, "detachallthis", g_sPath); + } else if(sMsg == Checkbox(TRUE, "att. subfolder") || sMsg == Checkbox(FALSE, "att. subfolder")){ + llMessageLinked(LINK_SET, SET_FOLDER_LOCK, "attachallthis", g_sPath); + } + + if(iRespring)llMessageLinked(LINK_SET, QUERY_FOLDER_LOCKS, g_sPath, ""); + } else if(sMenu == "Menu~Folders"){ + if(sMsg == "Browse"){ + Browser(kAv,iAuth,""); + iRespring=FALSE; + } else if(sMsg == CONFIG){ + ConfigureMenu(kAv,iAuth); + iRespring=FALSE; + } else if(sMsg == "Clear Locks"){ + llMessageLinked(LINK_SET, CLEAR_FOLDER_LOCKS, "", ""); + } else if(sMsg == UPMENU){ + iRespring=FALSE; + + llMessageLinked(LINK_SET, iAuth, "menu "+g_sParentMenu, kAv); + } + + + if(iRespring)Menu(kAv,iAuth); + } else if(sMenu == "Folders~Configure"){ + + // configuration menu + if(sMsg==UPMENU){ + iRespring=FALSE; + Menu(kAv,iAuth); + } else { + + list ButtonFlags = llParseString2List(sMsg,[" "],[]); + string ButtonLabel = llDumpList2String(llList2List(ButtonFlags,1,-1), " "); + integer Enabled = llListFindList(g_lFolderCheckboxes, [llList2String(ButtonFlags,0)]); + if(ButtonLabel=="Hide ~") + { + g_iHideTilde=!g_iHideTilde; + llMessageLinked(LINK_SET, LM_SETTING_SAVE, "folders_hidetilde="+(string)g_iHideTilde,""); + } + else if(Enabled){ + // Disable flag + if(ButtonLabel == "Trusted")g_iAccessBitSet -=1; + else if(ButtonLabel == "Public")g_iAccessBitSet-=2; + else if(ButtonLabel == "Group")g_iAccessBitSet-=4; + else if(ButtonLabel == "Wearer")g_iAccessBitSet-=8; + }else{ + if(ButtonLabel == "Trusted")g_iAccessBitSet+=1; + else if(ButtonLabel == "Public")g_iAccessBitSet+=2; + else if(ButtonLabel == "Group")g_iAccessBitSet+=4; + else if(ButtonLabel == "Wearer") g_iAccessBitSet+=8; + } + + // save + llMessageLinked(LINK_SET, LM_SETTING_SAVE, "folders_accessflags="+(string)g_iAccessBitSet, ""); + } + + if(iRespring) ConfigureMenu(kAv,iAuth); + } + } + } else if (iNum == DIALOG_TIMEOUT) { + integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); + g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex +3); //remove stride from g_lMenuIDs + } else if(iNum == LM_SETTING_RESPONSE){ + // Detect here the Settings + list lSettings = llParseString2List(sStr, ["_","="],[]); + + //integer ind = llListFindList(g_lSettingsReqs, [llList2String(lSettings,0)+"_"+llList2String(lSettings,1)]); + // if(ind!=-1)g_lSettingsReqs = llDeleteSubList(g_lSettingsReqs, ind,ind); + + + if(llList2String(lSettings,0)=="global"){ + if(llList2String(lSettings,1)=="locked"){ + g_iLocked=llList2Integer(lSettings,2); + } else if(llList2String(lSettings,1) == "checkboxes"){ + g_lFolderCheckboxes = llParseString2List(llList2String(lSettings,2),[","],[])+["◑"]; + } + } else if(llList2String(lSettings,0)=="folders"){ + if(llList2String(lSettings,1) == "accessflags"){ + g_iAccessBitSet=(integer)llList2String(lSettings,2); + } else if(llList2String(lSettings,1) =="hidetilde"){ + g_iHideTilde=(integer)llList2String(lSettings,2); + } + + } + } else if(iNum == REPLY_FOLDER_LOCKS) + { + integer iMask = (integer)((string)kID); + LocksMenu(g_kMenuUser, g_iMenuAuth, iMask); + } + //llOwnerSay(llDumpList2String([iSender,iNum,sStr,kID],"^")); + } +} diff --git a/src/collar/oc_label.lsl b/src/collar/oc_label.lsl index 8b29a7ba1..46642287f 100644 --- a/src/collar/oc_label.lsl +++ b/src/collar/oc_label.lsl @@ -5,7 +5,7 @@ // Joy Stipe, Wendy Starfall, Romka Swallowtail, littlemousy, // Garvin Twine et al. // Licensed under the GPLv2. See LICENSE for full details. -string g_sScriptVersion = "8.1"; +string g_sScriptVersion = "8.3"; integer LINK_CMD_DEBUG=1999; DebugOutput(key kID, list ITEMS){ integer i=0; @@ -333,7 +333,7 @@ integer bool(integer a){ if(a)return TRUE; else return FALSE; } -list g_lCheckboxes=["⬜","⬛"]; +list g_lCheckboxes= ["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; } diff --git a/src/collar/oc_leash.lsl b/src/collar/oc_leash.lsl index 80f6d049e..b4b7995a1 100644 --- a/src/collar/oc_leash.lsl +++ b/src/collar/oc_leash.lsl @@ -1,10 +1,33 @@ -// This file is part of OpenCollar. -// Copyright (c) 2008 - 2016 Nandana Singh, Lulu Pink, Garvin Twine, -// Joy Stipe, Cleo Collins, Satomi Ahn, Master Starship, Toy Wylie, -// Kaori Gray, Sei Lisa, Wendy Starfall, littlemousy, Romka Swallowtail, -// Sumi Perl, Karo Weirsider, Kurt Burleigh, Marissa Mistwallow et al. -// Licensed under the GPLv2. See LICENSE for full details. -string g_sScriptVersion = "8.1"; + +/* This file is part of OpenCollar. + Copyright (c) 2008 - 2024 Nandana Singh, Lulu Pink, Garvin Twine, + Joy Stipe, Cleo Collins, Satomi Ahn, Master Starship, Toy Wylie, + Kaori Gray, Sei Lisa, Wendy Starfall, littlemousy, Romka Swallowtail, + Sumi Perl, Karo Weirsider, Kurt Burleigh, Marissa Mistwallow et al. + Licensed under the GPLv2. See LICENSE for full details. + +Medea (medea.destiny) + Nov 2023 - Added EXC_REFRESH call after releasing strict leash + to ensure that exceptions that should be in place get + restored. issue #1008 + Aug 2024 - Uses the above feature to add an accepttp function to + strict leash, so that the leash wearer. Notification now + sent to both leashee and leasher when leash is grabbed while + strict mode is active, informing them of restrictions/exceptions. + - Refactored timer event to fix bypassed awaycounter functionality + so that the strict leash restrictions do not instantly get removed + when leash holder is absent. Time buffer now functions as intended and + is extended from 15 to 60 seconds to give leash holder time after a + teleport to send leashee a tp lure. +Nikki Larima + Nov 2023 - Remove processing of "runaway" command string, handled by CMD_SATEWORD + implemented Yosty7b3's menu streamlining, see pr#963 + +Licensed under the GPLv2. See LICENSE for full details. +https://github.com/OpenCollarTeam/OpenCollar +*/ + +string g_sScriptVersion = "8.3"; integer LINK_CMD_DEBUG=1999; // ------ TOKEN DEFINITIONS ------ @@ -41,7 +64,7 @@ integer LM_SETTING_SAVE = 2000; integer LM_SETTING_REQUEST = 2001; integer LM_SETTING_RESPONSE = 2002; integer LM_SETTING_DELETE = 2003; -integer LM_SETTING_EMPTY = 2004; +integer LM_SETTING_EMPTY = 2004; // -- MENU/DIALOG integer MENUNAME_REQUEST = 3000; @@ -52,13 +75,14 @@ integer RLV_CMD = 6000; integer RLV_OFF = 6100; integer RLV_ON = 6101; +integer EXC_REFRESH=6109; // send to request exceptions are refreshed. integer LEASH_START_MOVEMENT = 6200; integer LEASH_END_MOVEMENT = 6201; integer DIALOG = -9000; integer DIALOG_RESPONSE = -9001; -integer DIALOG_TIMEOUT = -9002; +//integer DIALOG_TIMEOUT = -9002; integer SENSORDIALOG = -9003; integer CMD_PARTICLE = 20000; @@ -72,8 +96,6 @@ string BUTTON_SUBMENU = "Leash"; // --------------------------------------------- // ------ VARIABLE DEFINITIONS ------ // ----- menu ----- -list g_lMenuIDs; -integer g_iMenuStride = 3; integer g_iPreviousAuth; key g_kLeashCmderID; @@ -108,7 +130,7 @@ integer g_iStrictModeOn=FALSE; //default is Real-Leash OFF integer g_iTurnModeOn = FALSE; integer g_iLeasherInRange=FALSE; // integer g_iRLVOn=FALSE; // To store if RLV was enabled in the collar -integer g_iAwayCounter=0; +integer g_iAwayCounter=-1; @@ -138,20 +160,12 @@ string NameURI(key kID){ } Dialog(key kRCPT, string sPrompt, list lButtons, list lUtilityButtons, integer iPage, integer iAuth, string sMenuID) { - key kMenuID = llGenerateKey(); - llMessageLinked(LINK_SET, DIALOG, (string)kRCPT + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lButtons, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, kMenuID); - integer iIndex = llListFindList(g_lMenuIDs, [kRCPT]); - if (~iIndex) g_lMenuIDs = llListReplaceList(g_lMenuIDs, [kRCPT, kMenuID, sMenuID], iIndex, iIndex + g_iMenuStride - 1); - else g_lMenuIDs += [kRCPT, kMenuID, sMenuID]; + llMessageLinked(LINK_SET, DIALOG, (string)kRCPT + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lButtons, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, sMenuID+"~"+llGetScriptName()); } SensorDialog(key kRCPT, string sPrompt, string sSearchName, integer iAuth, string sMenuID, integer iSensorType) { - key kMenuID = llGenerateKey(); if (sSearchName != "") sSearchName = "`"+sSearchName+"`1"; - llMessageLinked(LINK_SET, SENSORDIALOG, (string)kRCPT +"|"+sPrompt+"|0|``"+(string)iSensorType+"`10`"+(string)PI+sSearchName+"|"+BUTTON_UPMENU+"|" + (string)iAuth, kMenuID); - integer iIndex = llListFindList(g_lMenuIDs, [kRCPT]); - if (~iIndex) g_lMenuIDs = llListReplaceList(g_lMenuIDs, [kRCPT, kMenuID, sMenuID], iIndex, iIndex + g_iMenuStride - 1); - else g_lMenuIDs += [kRCPT, kMenuID, sMenuID]; + llMessageLinked(LINK_SET, SENSORDIALOG, (string)kRCPT +"|"+sPrompt+"|0|``"+(string)iSensorType+"`10`"+(string)PI+sSearchName+"|"+BUTTON_UPMENU+"|" + (string)iAuth, sMenuID+"~"+llGetScriptName()); } ConfirmDialog(key kAv, key kCmdGiver, string sType, integer iAuth) { @@ -208,7 +222,9 @@ ApplyRestrictions() { if (g_kLeashedTo) { //Debug("Setting restrictions"); //llSay(0, "RLV_CMD issue: no fly, notp"); - llMessageLinked(LINK_SET, RLV_CMD, "fly=n,tplm=n,tplure=n,tploc=n,tplure:" + (string) g_kLeashedTo + "=add,fartouch=n,sittp=n", "realleash"); //set all restrictions + llMessageLinked(LINK_SET, RLV_CMD, "fly=n,tplm=n,tplure=n,tploc=n,tplure:" + (string) g_kLeashedTo + "=add,fartouch=n,sittp=n,accepttp:"+(string)g_kLeashedTo+"=add", "realleash"); + //set all restrictions + llMessageLinked(LINK_SET,NOTIFY,"1Strict leash mode active. If leash holder leaves range, restrictions will persist for 1 minute. Leash holder may force TP leashee after leaving range during that 1 minute period.",g_kLeashedTo); return; } @@ -220,6 +236,8 @@ ApplyRestrictions() { } //Debug("Releasing restrictions"); llMessageLinked(LINK_SET, RLV_CMD, "clear", "realleash"); //release all restrictions + llSleep(1); + if(g_iStrictModeOn) llMessageLinked(LINK_SET,EXC_REFRESH,"",""); } key g_kPassLeashFrom; list g_lPasslPoints; @@ -488,8 +506,6 @@ UserCommand(integer iAuth, string sMessage, key kMessageID, integer bFromMenu) { //LeashTo((key)sVal, kMessageID, iAuth, [], TRUE,0); } else SensorDialog(g_kCmdGiver, "\nWho shall be followed?\n", sVal,iAuth,"FollowTarget", AGENT); - } else if (sMessage == "runaway" && iAuth == CMD_OWNER) { - Unleash(kMessageID); } else if (sMessage == "unleash" || sMessage == "unfollow" || (sMessage == "toggleleash" && NULL_KEY != g_kLeashedTo)) { if (CheckCommandAuth(kMessageID, iAuth)) Unleash(kMessageID); if (bFromMenu) UserCommand(iAuth, "leashmenu", kMessageID ,bFromMenu); @@ -685,7 +701,8 @@ state active timer() { dtext("timer : ping"); //inlined old isInSimOrJustOutside function - if(g_bFollowMode){ + if(g_bFollowMode) + { dtext("Mode is follow"); } vector vLeashedToPos=llList2Vector(llGetObjectDetails(g_kLeashedTo,[OBJECT_POS]),0); @@ -693,9 +710,12 @@ state active if(vLeashedToPos == ZERO_VECTOR || llVecDist(llGetPos(), vLeashedToPos)> 255) iIsInSimOrJustOutside=FALSE; if (iIsInSimOrJustOutside && llVecDist(llGetPos(),vLeashedToPos)<(60+g_iLength)) { //if the leasher is now in range + dtext("timer : iIsInSimOrJustOutside && VecDist < (60+(iLength=3))"); - if(!g_iLeasherInRange) { //and the leasher was previously not in range - if (g_iAwayCounter) { + if(!g_iLeasherInRange) + { //and the leasher was previously not in range + if (g_iAwayCounter) + { g_iAwayCounter = -1; llSetTimerEvent(3.0); } @@ -713,27 +733,50 @@ state active ApplyRestrictions(); if(!g_iAlreadyMoving) llMessageLinked(LINK_SET, LEASH_START_MOVEMENT,"",""); - } else { + } + else + { dtext("timer : LeasherInRange = TRUE"); } - } else { //the leasher is not now in range + } + else + { //the leasher is not now in range dtext("timer : NotInSimOrOutside OR VecDist > (60+(iLength=3))"); - if(g_iLeasherInRange) { //but was a short while ago - if (g_iAwayCounter <= llGetUnixTime()) { + + if(g_iLeasherInRange) + { //but was a short while ago + + if(g_iAwayCounter==-1) + { + + //step 1, set AwayCounterto 0, 3 second timer. + llSetTimerEvent(3); + g_iAwayCounter=0; + dtext("Leash holder was previously in range"); + } + else if(g_iAwayCounter==0) + { + //3 seconds after first fail to locate, disable leash motion llTargetRemove(g_iTargetHandle); llStopMoveToTarget(); if(!g_bFollowMode) llMessageLinked(LINK_SET, CMD_PARTICLE, "unleash", g_kLeashedTo); - g_iLeasherInRange=FALSE; - ApplyRestrictions(); - g_iAwayCounter=-1; dtext("No leash holder in range\n* Stopping leash particles"); if(g_iAlreadyMoving)llMessageLinked(LINK_SET, LEASH_END_MOVEMENT,"",""); - } else if(g_iAwayCounter==-1){ - g_iAwayCounter = llGetUnixTime()+15; - dtext("Leash holder was previously in range"); + g_iAwayCounter=llGetUnixTime()+60; + g_iLeasherInRange=FALSE; + //set awaycounter to +60 seconds. } - } else { + } + else + { + if (g_iAwayCounter <= llGetUnixTime()) + { + //indicate out of range, clear realleash restrictions, reset awaycounter + + ApplyRestrictions(); + g_iAwayCounter=-1; + } // nothing else to do with the away counter // slow down the timer llSetTimerEvent(11); @@ -814,15 +857,14 @@ state active g_iRLVOn = FALSE; ApplyRestrictions(); } else if (iNum == DIALOG_RESPONSE) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kMessageID]); - if (~iMenuIndex) { + integer iPos = llSubStringIndex(kMessageID, "~"+llGetScriptName()); + if (iPos>0) { list lMenuParams = llParseString2List(sMessage, ["|"], []); key kAV = (key)llList2String(lMenuParams, 0); string sButton = llList2String(lMenuParams, 1); //integer iPage = (integer)llList2String(lMenuParams, 2); integer iAuth = (integer)llList2String(lMenuParams, 3); - string sMenu=llList2String(g_lMenuIDs, iMenuIndex + 1); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex - 2 + g_iMenuStride); + string sMenu = llGetSubString(kMessageID, 0, iPos-1); if (sMenu == "MainDialog"){ if (sButton == BUTTON_UPMENU) llMessageLinked(LINK_SET, iAuth, "menu "+BUTTON_PARENTMENU, kAV); @@ -870,9 +912,6 @@ state active g_kLeashCmderID = ""; } } - } else if (iNum == DIALOG_TIMEOUT) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kMessageID]); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex - 2 + g_iMenuStride); } else if (iNum == REBOOT && sMessage == "reboot") llResetScript(); else if(iNum == AUTH_REPLY){ list lParams = llParseString2List(sMessage, ["|"],[]); diff --git a/src/collar/oc_meshlabel.lsl b/src/collar/oc_meshlabel.lsl index fe4ff64e5..8f7afb20b 100644 --- a/src/collar/oc_meshlabel.lsl +++ b/src/collar/oc_meshlabel.lsl @@ -5,7 +5,9 @@ // Joy Stipe, Wendy Starfall, Romka Swallowtail, littlemousy, // Garvin Twine et al. // Licensed under the GPLv2. See LICENSE for full details. - +// +// Ping (Pingout Duffield) +// *Aug 2023 - Revise visibility when collar is hidden Ref Issue #932 // How to use: // // - You need a Mesh-Strip with 6 Faces next to each other. Each Face should have 1 Material. @@ -24,7 +26,7 @@ // ***************** ***************** ***************** string g_sAppVersion = "1.1"; -string g_sScriptVersion = "8.1"; +string g_sScriptVersion = "8.3"; string g_sParentMenu = "Apps"; string g_sSubMenu = "Label"; @@ -189,7 +191,7 @@ integer LabelsCount() { lLineLinks += [0]; // Fill with Zero g_lLabelLinks = llListReplaceList(g_lLabelLinks,[llDumpList2String(lLineLinks,"|")],iLine,iLine); //change prim description - llSetLinkPrimitiveParamsFast(iLink,[PRIM_DESC,"Label~notexture~nocolor~nohide~noshiny"]); + llSetLinkPrimitiveParamsFast(iLink,[PRIM_DESC,"Label~notexture"]); } else if (sLabel == "LabelBase") g_lLabelBaseElements += iLink; } @@ -327,7 +329,7 @@ integer bool(integer a){ if(a)return TRUE; else return FALSE; } -list g_lCheckboxes=["⬜","⬛"]; +list g_lCheckboxes=["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; } diff --git a/src/collar/oc_particle.lsl b/src/collar/oc_particle.lsl index bdf0f83ac..6cfbb6f5d 100644 --- a/src/collar/oc_particle.lsl +++ b/src/collar/oc_particle.lsl @@ -1,10 +1,15 @@ // This file is part of OpenCollar. -// Copyright (c) 2008 - 2017 Lulu Pink, Nandana Singh, Garvin Twine, +// Copyright (c) 2008 - 2023 Lulu Pink, Nandana Singh, Garvin Twine, // Cleo Collins, Satomi Ahn, Joy Stipe, Wendy Starfall, Romka Swallowtail, -// lillith xue, littlemousy et al. +// lillith xue, littlemousy, Nikki Lacrima et al. // Licensed under the GPLv2. See LICENSE for full details. +/* -string g_sScriptVersion = "8.1.0000"; + Nikki Lacrima + Aug 2023: Updated for lockguard chain texture + +*/ +string g_sScriptVersion = "8.3"; integer LINK_CMD_DEBUG=1999; //MESSAGE MAP //integer CMD_ZERO = 0; @@ -103,6 +108,7 @@ string g_sSettingToken = "particle_"; string g_sParticleTexture = "Silk"; string g_sParticleTextureID; //we need the UUID for llLinkParticleSystem +string g_sChainParticleTexture; //default chains for Lock Guard string g_sLeashParticleTexture; //string g_sOccParticleTexture = "4cde01ac-4279-2742-71e1-47ff81cc3529"; string g_sLeashParticleMode; @@ -244,7 +250,6 @@ key findPrimKey(string sDesc) } return NULL_KEY; } - doClearChain(string sChainCMD) { if (sChainCMD == "all") { @@ -259,7 +264,6 @@ doClearChain(string sChainCMD) list lChains = llParseString2List(sChainCMD,["~"],[]); // Could be a string like "point=target~point=target..." or "point~point..." integer i; for (i=0;i g_iLeashLength){ + return; + } + } + + iIsLinking = TRUE; + } else if (sLGCMD == "unlink") { + kLGTarget = NULL_KEY; // Request Target + if (sLGPoint == "all") { + integer iCPIndex = 0; + // loop over collar points and unleash if not leashed to avatar on fcollar + while (iCPIndex < llGetListLength(g_lCollarPoints)) { + integer iPrimIndex = llListFindList(g_lLeashPrims, [llList2String(g_lCollarPoints,iCPIndex)]); + integer iPrim = llList2Integer(g_lLeashPrims,iPrimIndex+1); + if (!g_iLeashActive || iPrim != iFCollarPrim) { + llLinkParticleSystem(iPrim,[]); + } + iCPIndex += 3; + } + } else { + if (!g_iLeashActive || iLeashPrim != iFCollarPrim) { + llLinkParticleSystem(iLeashPrim,[]); + } + } + } else if (sLGCMD == "gravity") { + g_vLeashGravity.z = -llList2Float(lLGCmd,iLGIndex++); + } else if (sLGCMD == "life") { + g_fParticleAge = llList2Float(lLGCmd,iLGIndex++); + } else if (sLGCMD == "color") { + g_vLeashColor.x = llList2Float(lLGCmd,iLGIndex++); + g_vLeashColor.y = llList2Float(lLGCmd,iLGIndex++); + g_vLeashColor.z = llList2Float(lLGCmd,iLGIndex++); + } else if (sLGCMD == "size") { + g_vLeashSize.x = llList2Float(lLGCmd,iLGIndex++); + g_vLeashSize.y = llList2Float(lLGCmd,iLGIndex++); + } else if (sLGCMD == "texture") { + g_sChainParticleTexture = llList2Key(lLGCmd,iLGIndex++); + } else if (sLGCMD == "ping") { + llWhisper( g_iChan_LOCKGUARD, "lockguard " + (string)llGetOwner() + " " + sLGPoint + " okay" ); + } else { + llWhisper(0, "Unknown LockGuard command: "+sLGCMD); + } } - if(llVecDist(llGetPos(), (vector)llList2String(llGetObjectDetails(kLGTarget, [OBJECT_POS]),0)) > g_iLeashLength){ - return; + if (iIsLinking) { + g_sParticleMode = "Classic"; + g_sParticleTextureID = g_sChainParticleTexture; + Particles(iLeashPrim, kLGTarget,g_vLeashSize); } - @ovLG; - g_iGotLMReplies=TRUE; - integer iIndex = llListFindList(g_lLeashPrims, [sLGPoint]); - if (iIndex > -1 && kLGAv == g_kWearer) { - if (sLGCMD == "link") Particles(llList2Integer(g_lLeashPrims,iIndex+1), kLGTarget,g_vLeashSize); - else if (sLGCMD == "unlink") llLinkParticleSystem(llList2Integer(g_lLeashPrims,iIndex+1),[]); - else if (sLGCMD == "gravity") g_vLeashGravity.z = llList2Float(lLGCmd,4); - else if (sLGCMD == "life") g_fParticleAge = llList2Float(lLGCmd,4); - else if (sLGCMD == "color") { - - g_vLeashColor.x = llList2Float(lLGCmd,4); - g_vLeashColor.y = llList2Float(lLGCmd,5); - g_vLeashColor.z = llList2Float(lLGCmd,6); - } else if (sLGCMD == "size") { - g_vLeashSize.x = llList2Float(lLGCmd,4); - g_vLeashSize.y = llList2Float(lLGCmd,5); - } else if (sLGCMD == "texture") g_sParticleTextureID = llList2Key(lLGCmd,4); - } } } else if (iChannel == g_iChan_LOCKMEISTER) { // Implementation of the LMV2 Protocol diff --git a/src/collar/oc_relay.lsl b/src/collar/oc_relay.lsl index c4c9d3bcd..42c9f96bf 100644 --- a/src/collar/oc_relay.lsl +++ b/src/collar/oc_relay.lsl @@ -19,7 +19,10 @@ Kristen Mynx, Phidoux (taya Maruti) Also removed DO_RLV_REFRESH which cleared all restrictions and exceptions. *July 2022 - Fixed bug: Ask mode only accepted one RLV command from the object. - + +Nikki Lacrima + *Nov 2023 - Remove extra CMD_SAFEWORD to CMD_RELAY_SAFEWORD processing + - implemented Yosty7b3's menu streamlining, see pr#963 et al. Licensed under the GPLv2. See LICENSE for full details. @@ -88,18 +91,13 @@ integer bool(integer a){ if(a)return TRUE; else return FALSE; } -list g_lCheckboxes=["▢", "▣"]; +list g_lCheckboxes=["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; } Dialog(key kID, string sPrompt, list lChoices, list lUtilityButtons, integer iPage, integer iAuth, string sName) { - key kMenuID = llGenerateKey(); - llMessageLinked(LINK_SET, DIALOG, (string)kID + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lChoices, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, kMenuID); - - integer iIndex = llListFindList(g_lMenuIDs, [kID]); - if (~iIndex) g_lMenuIDs = llListReplaceList(g_lMenuIDs, [kID, kMenuID, sName], iIndex, iIndex + g_iMenuStride - 1); - else g_lMenuIDs += [kID, kMenuID, sName]; + llMessageLinked(LINK_SET, DIALOG, (string)kID + "|" + sPrompt + "|" + (string)iPage + "|" + llDumpList2String(lChoices, "`") + "|" + llDumpList2String(lUtilityButtons, "`") + "|" + (string)iAuth, sName+"~"+llGetScriptName()); } integer g_iWearer=TRUE; // Lockout wearer option @@ -206,8 +204,6 @@ string tf(integer a){ else return "false"; } key g_kWearer; -list g_lMenuIDs; -integer g_iMenuStride; list g_lOwner; list g_lTrust; list g_lBlock; @@ -423,10 +419,9 @@ state active //We're hard coding page 0 because new menu calls should always be page 0 if(llSubStringIndex(sStr, g_sSubMenu + "|0|" + (string)CMD_EVERYONE) != -1) llMessageLinked(LINK_SET, CMD_ZERO, "menu "+g_sParentMenu, llGetSubString(sStr, 0, 35)); - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - if(iMenuIndex!=-1){ - string sMenu = llList2String(g_lMenuIDs, iMenuIndex+1); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex-1, iMenuIndex-2+g_iMenuStride); + integer iPos = llSubStringIndex(kID, "~"+llGetScriptName()); + if(iPos>0){ + string sMenu = llGetSubString(kID, 0, iPos-1); list lMenuParams = llParseString2List(sStr, ["|"],[]); key kAv = llList2Key(lMenuParams,0); string sMsg = llList2String(lMenuParams,1); @@ -527,10 +522,6 @@ state active } } } - - } else if (iNum == DIALOG_TIMEOUT) { - integer iMenuIndex = llListFindList(g_lMenuIDs, [kID]); - g_lMenuIDs = llDeleteSubList(g_lMenuIDs, iMenuIndex - 1, iMenuIndex +3); //remove stride from g_lMenuIDs } else if(iNum == LM_SETTING_RESPONSE){ // Detect here the Settings list lSettings = llParseString2List(sStr, ["_","="],[]); @@ -595,8 +586,8 @@ state active } } } else if(iNum == CMD_SAFEWORD){ - // Process safeword - llMessageLinked(LINK_SET, CMD_RELAY_SAFEWORD, "safeword", ""); + // Process safeword, can be removed, is done by SafeWord() in oc_rlvsys sending RLV_CLEAR + //llMessageLinked(LINK_SET, CMD_RELAY_SAFEWORD, "safeword", ""); } else if(iNum == RLV_CLEAR){ llMessageLinked(LINK_SET, CMD_RELAY_SAFEWORD, "",""); } else if(iNum == CMD_RELAY_SAFEWORD){ diff --git a/src/collar/oc_resizer.lsl b/src/collar/oc_resizer.lsl index fd52109b5..4d4c8a1f7 100644 --- a/src/collar/oc_resizer.lsl +++ b/src/collar/oc_resizer.lsl @@ -6,9 +6,11 @@ // Based on a split of OpenCollar - appearance by Romka Swallowtail -// Virtual Disgrace - Resizer is derivative of OpenCollar - adjustment +// Resizer is derivative of OpenCollar - adjustment -string g_sScriptVersion = "8.1.0000"; +// Ping (Pingout Duffield) Jan 2023 Fix behavior of BACK button in Position, Rotation, and Size menus + +string g_sScriptVersion = "8.3.0000"; integer LINK_CMD_DEBUG=1999; string g_sSubMenu = "Size/Position"; @@ -274,7 +276,7 @@ state active else if (sMessage == SIZEMENU) SizeMenu(kAv, iAuth); } else if (sMenuType == POSMENU) { if (sMessage == UPMENU) { - llMessageLinked(LINK_SET, iAuth, "menu " + g_sParentMenu, kAv); + DoMenu(kAv, iAuth); //llMessageLinked(LINK_SET, iAuth, "menu " + g_sParentMenu, kAv); return; } else if (llGetAttached()) { if (sMessage == "forward ↳") AdjustPos(); @@ -290,7 +292,7 @@ state active PosMenu(kAv, iAuth); } else if (sMenuType == ROTMENU) { if (sMessage == UPMENU) { - llMessageLinked(LINK_SET, iAuth, "menu " + g_sParentMenu, kAv); + DoMenu(kAv, iAuth); //llMessageLinked(LINK_SET, iAuth, "menu " + g_sParentMenu, kAv); return; } else if (llGetAttached()) { if (sMessage == "tilt right ↘") AdjustRot(); @@ -303,7 +305,7 @@ state active RotMenu(kAv, iAuth); } else if (sMenuType == SIZEMENU) { if (sMessage == UPMENU) { - llMessageLinked(LINK_SET, iAuth, "menu " + g_sParentMenu, kAv); + DoMenu(kAv, iAuth); //llMessageLinked(LINK_SET, iAuth, "menu " + g_sParentMenu, kAv); return; } else { integer iMenuCommand = llListFindList(SIZEMENU_BUTTONS, [sMessage]); diff --git a/src/collar/oc_rlvextension.lsl b/src/collar/oc_rlvextension.lsl index 976d75d7a..fa1b7fbab 100644 --- a/src/collar/oc_rlvextension.lsl +++ b/src/collar/oc_rlvextension.lsl @@ -46,7 +46,14 @@ Medea Destiny - settings to be restored to defaults if trigged before settings are received. (fixes #740, #720, #719) Aug2022 - Fix auth filtering for changing exceptions. Issue #844 & #848 - + Nov 2023 - Added EXC_REFRESH link message capability to request all exceptions are refreshed. + This to fix real leash temporary exception removing permanent exception, but likely to + find other uses. This is less optimal than having a way to refresh individual exceptions, + or even better having this script handle multiple source exceptions the way rlv_sys handles + restrictions. However that's something to consider for 9.x where linkset data can reduce + memory load, this one's already very tight. Issue #1008 + - folded bool() function into checkbox() function with (iValue&1) and strReplace() into + MuffleText() to save memory Krysten Minx - May2022 - Added check for valid UUID when setting custom exception */ @@ -90,6 +97,8 @@ integer RLV_REFRESH = 6001;//RLV plugins should reinstate their restrictions upo integer RLV_CMD_OVERRIDE=6010; // one-shot (=force) command that will override restrictions. Only use if Auth is owner level integer RLV_OFF = 6100; // send to inform plugins that RLV is disabled now, no message or key needed integer RLV_ON = 6101; // send to inform plugins that RLV is enabled now, no message or key needed +integer EXC_REFRESH=6109; // send to request exceptions are refreshed. + integer DIALOG = -9000; integer DIALOG_RESPONSE = -9001; @@ -111,13 +120,10 @@ key g_kWearer; string g_sCameraBackMenu="menu manage"; //for allowing the camera settings menu to be in 2 places. -integer bool(integer a){ - if(a)return TRUE; - else return FALSE; -} -list g_lCheckboxes=["⬜","⬛"]; + +list g_lCheckboxes= ["□","▣"]; string Checkbox(integer iValue, string sLabel) { - return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; + return llList2String(g_lCheckboxes, (iValue>0))+" "+sLabel; } list lRLVEx = [ @@ -158,27 +164,29 @@ integer samelist(list a, list b) //Returns TRUE if a and b are identical list g_lMuffleReplace = [ "p" , "h", + "P" , "H", "t" , "k", + "T" , "K", "m" , "n", + "M" , "N", "o" , "e", + "O" , "E", "u" , "o", + "U" , "O", "w" , "o", - "b" , "h" + "W" , "O", + "b" , "h", + "B" , "H" ]; integer g_iMuffleListener; -string strReplace(string str, string search, string replace) -{ - return llDumpList2String(llParseStringKeepNulls((str = "") + str, [search], []), replace); -} - string MuffleText(string sText) { integer i; for (i=0; i=31){ @@ -490,7 +519,7 @@ UserCommand(integer iNum, string sStr, key kID) { if (llToLower(sStr)=="restrictions" || llToLower(sStr) == "menu restrictions") Menu(kID, iNum); else if(llToLower(sStr)=="advanced" || llToLower(sStr) == "menu [advanced]")MenuDetailed(kID, iNum); else if(llToLower(sStr)=="customize" || llToLower(sStr) == "menu customize") MenuManage(kID,iNum); - else if(llToLower(sStr)=="list presets") { + else if(llToLower(sStr)=="list presets") { integer x=llGetListLength(g_lMacros); integer i; string out="0List of preset buttons, and what individual restrictions they apply:"; @@ -503,11 +532,14 @@ UserCommand(integer iNum, string sStr, key kID) { sStr=llGetSubString(sStr,14,-1); if(~llListFindList(g_lCategory,[sStr])) MenuCategory(kID,iNum,sStr); } - if (llSubStringIndex(sStr,"preset") && llSubStringIndex(sStr,"restriction") && llSubStringIndex(sStr,"restrictions") && llSubStringIndex(sStr,"sit")) return; + //if (llSubStringIndex(sStr,"preset") && llSubStringIndex(sStr,"restriction") && llSubStringIndex(sStr,"restrictions") && llSubStringIndex(sStr,"sit") && llSubStringIndex(sStr,"rlv") return; else { - string sChangetype = llList2String(llParseString2List(sStr, [" "], []),0); - string sChangekey = llList2String(llParseString2List(sStr, [" "], []),1); - string sChangevalue = llList2String(llParseString2List(sStr, [" "], []),2); + list lCommands=llParseString2List(sStr,[" "],[]); + string sChangetype = llToLower(llList2String(lCommands,0)); + if(llListFindList(["preset","restriction","restrictions","sit","rlv"],[sChangetype])==-1) return; + string sChangekey = llList2String(lCommands,1); + string sChangevalue = llDumpList2String(llList2List(lCommands,2,-1)," "); + lCommands=[]; //llOwnerSay(sChangetype+" | "+sChangekey); if (sChangetype == "preset") { integer iIndex = llListFindList(g_lMacros,[sChangevalue]); @@ -528,12 +560,13 @@ UserCommand(integer iNum, string sStr, key kID) { } } else llMessageLinked(LINK_SET, NOTIFY, "0"+"Insufficient authority to set restrictions!", kID); } else llInstantMessage(kID,"Restriction preset '"+sChangevalue+"' does not exist!"); - } else if (sChangetype == "restriction" || sChangetype == "rlv") { + } else if (sChangetype == "restriction" || sChangetype == "rlv" || sChangetype=="restrictions") { + if(sChangekey=="on" || sChangekey =="off" || sChangekey=="") return; if (sChangekey == "add") ApplyCommand(sChangevalue,TRUE, kID, iNum); else if (sChangekey == "rem" && iNum != CMD_WEARER) ApplyCommand(sChangevalue,FALSE, kID, iNum); else if (sChangekey == "list") llMessageLinked(LINK_SET,NOTIFY,"0Current Restrictions:\n"+llDumpList2String(ListRestrictions(g_iRestrictions1,g_iRestrictions2),"\n"),kID); - } else if (sChangetype == "restrictions") Menu(kID,iNum); + } //else if (sChangetype == "restrictions") Menu(kID,iNum); } } diff --git a/src/collar/oc_rlvsys.lsl b/src/collar/oc_rlvsys.lsl index 89af46869..9d087677c 100644 --- a/src/collar/oc_rlvsys.lsl +++ b/src/collar/oc_rlvsys.lsl @@ -19,7 +19,7 @@ Kristen Mynx - the relay, which is being changed at the same time. Check the comments in oc_relay. */ -string g_sScriptVersion = "8.2"; +string g_sScriptVersion = "8.3"; integer g_iRLVOn = TRUE; integer g_iRLVOff = FALSE; integer g_iViewerCheck = FALSE; diff --git a/src/collar/oc_settings.lsl b/src/collar/oc_settings.lsl index d87ac4a80..634941459 100644 --- a/src/collar/oc_settings.lsl +++ b/src/collar/oc_settings.lsl @@ -466,7 +466,7 @@ default FindLeashpointOrLock(); RestoreWeldState(); - if (!SettingExists("global_checkboxes")) g_lSettings = SetSetting("global_checkboxes", "▢,▣"); + if (!SettingExists("global_checkboxes")) g_lSettings = SetSetting("global_checkboxes", "□,▣"); if(llGetInventoryType(g_sSettings)!=INVENTORY_NONE){ g_iSettingsRead=0; diff --git a/src/collar/oc_titler.lsl b/src/collar/oc_titler.lsl index 5365fd51b..8cef548db 100644 --- a/src/collar/oc_titler.lsl +++ b/src/collar/oc_titler.lsl @@ -15,6 +15,7 @@ Medea (Medea Destiny) provide colour menu rather than setting colour to black, and means that "[prefix] title color red" will now pop up the color menu rather than setting the title text to black. Text changes now set instantly rather than after 2.5 second delay. + Sept 2023 - Zero length titles via chat commands (i.e [prefix]title) were returning "title" as a title rather than opening the textbox as intended. et al. Licensed under the GPLv2. See LICENSE for full details. https://github.com/OpenCollarTeam/OpenCollar @@ -22,7 +23,7 @@ https://github.com/OpenCollarTeam/OpenCollar string g_sParentMenu = "Apps"; string g_sSubMenu = "Titler"; -string g_sVersion = "8.2"; // leave unmodified if not changed at all after release, otherwise change to next version number +string g_sVersion = "8.3"; // leave unmodified if not changed at all after release, otherwise change to next version number DebugOutput(key kID, list ITEMS){ integer i=0; @@ -83,7 +84,7 @@ integer bool(integer a){ if(a)return TRUE; else return FALSE; } -list g_lCheckboxes=["⬜","⬛"]; +list g_lCheckboxes=["□","▣"]; string Checkbox(integer iValue, string sLabel) { return llList2String(g_lCheckboxes, bool(iValue))+" "+sLabel; } @@ -160,12 +161,11 @@ UserCommand(integer iNum, string sStr, key kID) { if(iNum !=CMD_OWNER)return; if(sChangetype == "title"){ - string sTitle = llDumpList2String(llList2List(llParseString2List(sStr,[" "],[]), 1,-1)," "); - if(sTitle == ""){ + if(sChangevalue == ""){ Dialog(kID, "What should the title say?", [], [], 0, iNum, "Textbox~Title"); return; } - g_sTitle=sTitle; + g_sTitle=llDumpList2String(llList2List(llParseString2List(sStr,[" "],[]), 1,-1)," "); Save(SAVE_TITLE); } else if(sChangetype == "titler"){ if(sChangevalue == "color"){ diff --git a/src/cuffs/oc_cuff.lsl b/src/cuffs/oc_cuff.lsl index 09325cdcd..ea8287fc6 100644 --- a/src/cuffs/oc_cuff.lsl +++ b/src/cuffs/oc_cuff.lsl @@ -1,3 +1,4 @@ + /* This file is a part of OpenCollar. Copyright ©2021 @@ -17,6 +18,7 @@ Kristen Mynx * July 2022 - Fix "BACK" buttons on resizer Ping (Pingout Duffield) * Nov 2022 - Add in link message for cuff [New Theme] button using iNum == 32 + * May 2023 - Minor change: new chain texture UUID, adjustment to particle scale et al. Licensed under the GPLv2. See LICENSE for full details. https://github.com/OpenCollarTeam/OpenCollar @@ -288,14 +290,14 @@ ClearAllParticles(){ SetParticles(integer link, key kID,key kTexture, float fMaxAge, float fGravity){ - if(kTexture=="" || kTexture=="def")kTexture="4cde01ac-4279-2742-71e1-47ff81cc3529"; + if(kTexture=="" || kTexture=="def")kTexture="1f5df35f-7859-897a-9c40-c787ba944393"; if(fMaxAge==0)fMaxAge=7.3; if(llRound(fGravity) == -1) fGravity = -0.01; llLinkParticleSystem(link, [ PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_DROP, PSYS_PART_START_ALPHA,1, -PSYS_PART_START_SCALE,<0.075, 0.075, 0>, -PSYS_PART_END_SCALE,<0.075,0.075,0>, +PSYS_PART_START_SCALE,<0.07, 0.07, 0>, +PSYS_PART_END_SCALE,<0.07,0.07,0>, PSYS_PART_MAX_AGE,fMaxAge, PSYS_SRC_BURST_PART_COUNT,1, PSYS_SRC_ACCEL,<0, 0, -0.01>, diff --git a/src/installer/BUNDLE_40-Apps_OPTIONAL b/src/installer/BUNDLE_40-Apps_OPTIONAL index bc86fd3a8..eb56c8849 100644 --- a/src/installer/BUNDLE_40-Apps_OPTIONAL +++ b/src/installer/BUNDLE_40-Apps_OPTIONAL @@ -7,4 +7,5 @@ SCRIPT|oc_undress SCRIPT|oc_detach SCRIPT|oc_outfits SCRIPT|oc_bell -SCRIPT|oc_folders \ No newline at end of file +SCRIPT|oc_folders +SCRIPT|oc_timer diff --git a/src/installer/oc_installer_sys.lsl b/src/installer/oc_installer_sys.lsl index f1beb35e5..b12862077 100644 --- a/src/installer/oc_installer_sys.lsl +++ b/src/installer/oc_installer_sys.lsl @@ -218,8 +218,9 @@ default { } touch_start(integer iNumber) { - llWhisper(0, "Hello! In your collar menu, go to Help/About and press Update to begin the update"); + llOwnerSay("Hello! In your collar menu, go to Help/About and press Update to begin the update"); return; + //If we weren't planning to redo this for 9.x anyway I'd scream right here. if (llDetectedKey(0) != llGetOwner()) return; if (g_iDone) { g_iDone = FALSE; @@ -311,7 +312,7 @@ default { on_rez(integer iStartParam) { string sPrefix = llToLower(llGetSubString(llKey2Name(llGetOwner()),0,1)); - llSay(0, "Thank you for rezzing me. Next: In the Collar menu, go to Help/About and press Update. Or, use the chat command '"+sPrefix+" update'."); + llOwnerSay("Thank you for rezzing me. Next: In the Collar menu, go to Help/About and press Update. Or, use the chat command '"+sPrefix+" update'."); llResetScript(); } no_sensor() diff --git a/src/spares/oc_grabbypost.lsl b/src/spares/oc_grabbypost.lsl.DEPRECATED similarity index 97% rename from src/spares/oc_grabbypost.lsl rename to src/spares/oc_grabbypost.lsl.DEPRECATED index 1fd6d3252..18e906032 100644 --- a/src/spares/oc_grabbypost.lsl +++ b/src/spares/oc_grabbypost.lsl.DEPRECATED @@ -5,6 +5,9 @@ // Needs OpenCollar 6.x or higher to work +//NO LONGER WORKS! THIS FILE IS DEPRECATED, PLEASE SEE OC_SMART_POST INSTEAD +//THIS FILE IS RETAINED IN CASE SOMEONE WOULD LIKE TO UPDATE IT AT SOME POINT + // Sends a ":ping" message on the collar command channel: // Collar answers with ":pong" // Sends a anchor command on the collar command channel to grab them diff --git a/src/spares/oc_leash_on_collision b/src/spares/oc_leash_on_collision new file mode 100644 index 000000000..0e439e5b9 --- /dev/null +++ b/src/spares/oc_leash_on_collision @@ -0,0 +1,20 @@ +/* +Update to Lulu Pink's grabby script for OC8.3+ interface channel + + +*/ +integer getChannel(key kAv) +{ + integer chan= -llAbs((integer)("0x" + llGetSubString(kAv,30,-1))); + if(chan==0) chan= -9876; //I mean it COULD happen. + return chan; +} + +default +{ + collision_start(integer num) + { + integer iChannel = getChannel(llDetectedKey(0)); + llSay(iChannel, (string)llDetectedKey(0) + ":anchor " + (string)llGetKey()); + } +} diff --git a/src/spares/oc_smart_post b/src/spares/oc_smart_post new file mode 100644 index 000000000..6a55672a1 --- /dev/null +++ b/src/spares/oc_smart_post @@ -0,0 +1,374 @@ +/* +This file is a part of OpenCollar. +Created by Medea Destiny copyright 2023 +Licensed under the GPLv2. See LICENSE for full details. +https://github.com/OpenCollarTeam/OpenCollar + + +This script is intended as an example to demonstrate more complicated +applications of the OC interface channel. Until OC8.3, the interface channel +has been used for some very basic inter-object communication -- mainly collars +sending a ping to check that they are the only collar worn, and triggering +extra collars to autodetach (formerly known as the highlander system). +Previously there was also a hudchannel which was used by the OpenCollar Remote +to send commands directly to the collar, but this functionality got wrapped into +the interface channel a while back, and then lost when the OC Remote started using +the add-on interface. + +The interface channel already supports the menuto command, whereby the collar +can be asked to send a menu to someone with the command: +menuto:target_key + +This is used by the oc_wearer_hud, a simple script that can be dropped into a prim +to make a very basic script that allows the wearer to click a hud button to bring +up their collar menu rather than having to type in a chat command or click their +collar. + +As of 8.3 the interface channel's ability to process chat commands has been restored +and enhanced. This allows objects to communicate with OpenCollar without having to +establish a connection between the object and the collar via the add-on system, and +is more suitable than add-ons for uses where the object is not intended to share +variables with the collar, or instert a menu into the collar's own menus. + +The interface channel is a private channel which each collar listens +to, and the channel number will vary from collar to collar, as it is +derived from the wearer's UUID. The function getChannel(key kAv) below +shows how to derive the personal channel for any person. + +Commands are sent to the collar's interface channel in the format + +target_key:command + +where targetkey is the uuid of the target and command is the command +sequence that will be sent by the collar as a linked message, as if it +had been issued via chat command. Anything that will work as a chat command +i.e /1 (prefix) command +can function through the interface channel. + +Auth for commands will normally be processed as the auth for the owner of the object. +However it is possible to prefix your command with: +authas:operator_key=target_key:command +In this case, the collar will use whichever is LOWER of the auth level of the object +owner and operator_key. Note that as a security measure this will only function when +operator_key is found in the sim. +For example, consider an object with this simple script: + +default +{ + touch_start(integer number) + { + llSay(getChannel(llGetOwner()),(string)llGetOwner()+":kneel"); + } +} + +When clicked, the object will chat on the object owner's channel with the +owner's key followed by ":kneel", which will send the kneel command to the +owner's collar. The kneel command will be authed with the owner of the object, +and as collar wearers can trigger a kneel command in themselves, it will always +work. + +However if we change the line to: + + llSay(getChannel(llGetOwner()),"authas:"+(string)llDetectedKey(0)+"="+(string)llGetOwner()+":kneel"); +...the command will now be authed according to the auth level of the person touching +the object, and the auth level returned will be whichever has LEAST authority, between the +person touching and the owner of the object. So if the collar is on public, anyone can use +the object to trigger the collar wearer to kneel, while if the collar isn't then anyone +with owner or trusted permission on the collar can, but other people can't. + +Another function that the interface channel handles is CHECKAUTH, which will cause the collar to +reply with the auth level the object has on a specified channel. For example: + +llSay(getChannel(target_key),"checkauth 1111"); + +... will cause the collar of the target_key to reply on channel 1111 with: +"authreply|target_key|(auth level)|(key of person authed)" + +where auth level is the auth of the owner of the object sending it +(so long as the owner of the object has any access at all -- the collar +simply won't reply if auth doesn't pass). Normally the key of the person +authed will be the owner of the object sending the message. However this +can be combined with authas as follows: + +llSay(getChannel(target_key),"authas:(test_key):checkauth 1111"); + +...in this case, the collar will reply with the auth level of test_key, so: +"authreply|target_key|(auth level)|test_key" + +The idea behind this system is that objects can act with the auth +of the person operating them rather than the auth of the object owner. +Note that regardless of the auth level of the test subject, this will +only work if the owner of the object has sufficient auth to get a menu. +This will never pass an auth level that is greater than the owner of the +object. For example if an object is owned by someone with trusted +permission, even if the person operating the object has owner permission, +the auth level returned will be trusted. + +LIMITATION: the system sends link messages with the auth level returned by +authas, but it will send linked messages with the owner of the sending object +as id, noth the authas key. While this has some downsides such as not allowing +someone else's object to provide you with a menu, it's required to ensure commands +are not spoofed. + +*/ + +integer getChannel(key kAv) //function to return interface channel of target +{ + integer chan= -llAbs((integer)("0x" + llGetSubString(kAv,30,-1))); + if(chan==0) chan= -9876; //I mean it COULD happen. + return chan; +} + +integer iTime=5; //This is how long we're going to wait for replies from collars when scanning for a target. + +key kTarget; //Once picked, target's key stored here. +integer iTargetChan; // and their interface channel stored here +string sTargetInfo; //Info including user's access level stored here for use in the menu + +integer iHandle; //dialog stuff +integer iChan; + +integer iScan; //TRUE while a scan is in progress. + +integer iMenuType; //1 for a scan menu to select target, 0 for main menu + +list lHits; //temporary list which stores people found who have an accessible collar + +key kOperator; //Key of person using device. + + +integer iLeashed; //These three variables store whether these actions have been done yet. +integer iRestricted; +integer iKneeling; + +scan() +{ + kTarget=NULL_KEY; //Blank all target info, we're getting a new target. + iTargetChan=-1; + sTargetInfo=""; + + llRegionSayTo(kOperator,0,"Scanning for accessible collars. Please wait "+(string)iTime+" seconds."); + iScan=TRUE; //this tells the script it's mid-scan; + lHits=[]; + list t1=llGetAgentList(AGENT_LIST_PARCEL,[]); //Get List of Agents in Parcel + list t2; + vector pos; + vector mypos=llGetPos(); + while(llGetListLength(t1)) //Create new list in form distance, key + { + pos=llList2Vector(llGetObjectDetails(llList2Key(t1,0),[OBJECT_POS]),0); + t2+=[llVecDist(pos,mypos),llList2Key(t1,0)]; + t1=llDeleteSubList(t1,0,0); + } + t2=llListSort(t2,2,TRUE); // sort list by distance + if(llGetListLength(t2)>16) t2=llList2List(t2,0,15); //trim list to 8 nearest keys (16 entries of key/distance pairs + iChan=(integer)llFrand(9999)+9999; + iHandle=llListen(iChan,"","",""); + llSetTimerEvent(iTime); + while(llGetListLength(t2)) + { + key try=llList2Key(t2,1); //key is the second element in the distance/key pair + llRegionSay(getChannel(try),"authas:"+(string)kOperator+":checkauth="+(string)iChan); //send checkauth command for each found key + t2=llDeleteSubList(t2,0,1); + } +} +menu(key target) +{ + if(target!=kOperator) return; + iMenuType=0; + iChan=(integer)llFrand(9999)+9999; + iHandle=llListen(iChan,"",target,""); + llSetTimerEvent(60); + list buttons; + if(!iLeashed) buttons+="Leash"; //Add these buttons if the function hasn't already been used. + if(!iRestricted) buttons+="Restrict"; + if(!iKneeling) buttons+="Kneel"; + if(iKneeling||iRestricted||iLeashed) buttons+="Release"; //Add release button if anything is set + buttons=["Quit","New Target"]+buttons; + llDialog(target,sTargetInfo+"\nPick something to do to target!",buttons,iChan); +} +default +{ + state_entry() + { + + } + + touch_start(integer total_number) + { + if(iScan) + { + llRegionSayTo(llDetectedKey(0),0,"Busy scanning, wait a few seconds please!"); + return; + } + if(llDetectedKey(0)!=kOperator) //New user, rescan for targets + { + kOperator=llDetectedKey(0); //store the key of the person operating the device. + scan(); + } + else menu(kOperator); //Current operator has clicked again, just give 'em a menu! + } + listen(integer iChannel, string sName, key kID, string sMsg) + { + if(iScan) + { + list t=llParseString2List(sMsg,["|"],[]); + if(llList2String(t,0)=="authreply") //reply from collar in the form authreply|Wearer_Key|auth_level|operator key + { + if(kOperator!=llList2Key(t,3)) + { + //TRUE if message is not intended for current operator. Something went wrong, we start again. + llRegionSayTo(kOperator,0,"Scan failed! Please try again in a moment."); + kOperator=NULL_KEY; + llSetTimerEvent(0); + llListenRemove(iHandle); + iScan=FALSE; + llSleep(3); + return; + } + + if(llGetOwnerKey(kID)==llList2Key(t,1)) //this should always be true, but we double-check + { + integer auth=(integer)llList2String(t,2); + if(auth==500) lHits+=["Owner",llList2Key(t,1)]; + else if(auth==501) lHits+=["Trusted",llList2Key(t,1)]; + else if(auth==502) lHits+=["Group",llList2Key(t,1)]; + else if(auth==503) lHits+=["Wearer",llList2Key(t,1)]; + else if(auth==504) lHits+=["Public",llList2Key(t,1)]; + //added a pair of values to the hits list auth level, followed by wearer key + } + } + } + else + { + llSetTimerEvent(0); + llListenRemove(iHandle); + if(iMenuType==1) //Scan results menu. Here we pick a target. + { + if(sMsg=="rescan") + { + scan(); + return; + } + iScan=FALSE; //We're no longer scanning. + if(sMsg=="QUIT") return; + else + { + //A button was clicked representing someone on the lHits hitlist. Find out who + //and set them as a target. + integer index=(integer)sMsg; + if(index) + { + index=index*2-1; + kTarget=llList2String(lHits,index); //Set the target; + iTargetChan=getChannel(kTarget); //Set the target's interface channel + sTargetInfo="Target: secondlife:///app/agent/"+(string)kTarget+"/about\nYour auth level is: "+llList2String(lHits,index-1)+"."; + menu(kID); + } + lHits=[]; + } + } + else if(iMenuType==0) //Command menu, once a target has been picked. + { + if(sMsg=="New Target") + { + //send the scan command again. As the user is the same we don't need to change kOperator + scan(); + return; + } + if(sMsg=="Quit") return; + else if(sMsg=="Leash") + { + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":anchor "+(string)llGetKey()); + iLeashed=TRUE; + //We send the command with authas:(operator)=(targetkey):command. Here command is post (obect key) + //which will order the collar to leash to the object sending the command. + } + else if(sMsg=="Kneel") + { + iKneeling=TRUE; + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":kneel"); + //This one is very simple, we're just sending the kneel command. Same as chatting "(prefix) kneel" + } + else if(sMsg=="Restrict") + { + iRestricted=TRUE; + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":restriction add Sit TP"); + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":restriction add Landmark"); + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":restriction add TP Location"); + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":restriction add Accept TP"); + //Although this is a bit more complicated, it's all the same principle. This is really just the same as + //using chat commands to add restrictions, one after the other. Note that you can (as of 8.3) use the + //actual RLV commands (for example tploc rather that TP Location, but we're using the button names from the + //restrictions menu here. + } + else if(sMsg=="Release") + { + if(iKneeling) + { + iKneeling=FALSE; + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":stop"); + //Clear the variable and send the chat command to stop kneeling. + } + if(iLeashed) + { + iLeashed=FALSE; + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":unleash"); + //Clear the variable and send the chat command to unleash + } + if(iRestricted) + { + iRestricted=FALSE; + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":restriction rem Sit TP"); + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":restriction rem Landmark"); + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":restriction rem TP Location"); + llRegionSayTo(kTarget,iTargetChan,"authas:"+(string)kID+"="+(string)kTarget+":restriction rem Accept TP"); + //You get the drill by now! + } + } + menu(kID); //Refresh the menu. + + + } + } + } + timer() + { + llSetTimerEvent(0); + llListenRemove(iHandle); + if(iScan) + { + //timer while listening for checkauth responses is up. + iScan=FALSE; + if(llGetListLength(lHits)==0) llRegionSayTo(kOperator,0,"Sorry, there's nobody on the parcel wearing an OC8.3 or newer collar that you have access to."); + else + { + //We've got at least one collar response, so create a menu to allow user to pick their target from the options + iChan=-1000-(integer)llFrand(99999); + list buttons=["1","2","3","4","5","6","7","8"]; + integer i=llGetListLength(lHits); + buttons=llList2List(buttons,0,(i/2)-1); //trim button list to length of hit list; + if(i>2)buttons=llList2List(buttons,0,0)+["Rescan","Quit"]+llList2List(buttons,1,-1); + else buttons+=["Rescan","Quit"]; //put the rescan and quit buttons on the bottom row + string text="Pick a target. Options as follows: (access / name)\n"; + integer x; + while(x,1); - //llSetText("In use..", <1,0,0>,1); + // llSetText("In use..", <1,0,0>,1); g_iLMLastSent = llGetUnixTime(); g_kUser=llDetectedKey(0); API_CHANNEL = ((integer)("0x" + llGetSubString((string)g_kUser, 0, 8))) + 0xf6eb - 0xd2; @@ -241,12 +238,6 @@ default } if(sStr == "settings=sent"){ - if(g_iAddonLimitation){ - llSay(0, "Addons Limited is checked. To unweld, someone with Owner access (including wearer if they are Owner) must: 1) Open the collar menu. 2)Click Settings. 3) Click the Addons button in Settings. UNCHECK AddOns Limited. Leave WearerAdd and Addons CHECKED. The Wearer can then proceed to unweld."); - Link("offline", 0, "", g_kUser); - llSleep(2); - llResetScript(); - } else { llSay(0, "Checking for a existing collar weld"); if(g_iWelded){ llSay(0, "Unweld tool now ready."); @@ -259,6 +250,12 @@ default llSleep(2); llResetScript(); } + + if(g_iAddonLimitation){ + llSay(0, "Addons Limited is checked. To unweld, a collar owner (including unowned wearer) must go to the collar Settings menu and find the AddOn Settings button. Uncheck AddOns Limited and then proceed to unweld."); + Link("offline", 0, "", g_kUser); + llSleep(2); + llResetScript(); } } } @@ -292,7 +289,7 @@ default } else if (sMsg == "UNWELD NOW") { - if(iAuth == CMD_OWNER || g_kUser == llGetOwnerKey(id)){ + if(iAuth == CMD_OWNER || iAuth == CMD_WEARER){ Link("from_addon", NOTIFY_OWNERS, "The unweld tool was used.", ""); llSay(0, "Consent : Valid"); Link("from_addon", LM_SETTING_DELETE, "intern_weld","origin"); diff --git a/src/spares/oc_unwelder_hud.lsl b/src/spares/oc_unwelder_hud.lsl index 98a305646..ff031d717 100644 --- a/src/spares/oc_unwelder_hud.lsl +++ b/src/spares/oc_unwelder_hud.lsl @@ -6,7 +6,8 @@ Aria (Tashia Redrose) * Feb 2021 - Created oc_unwelder_hud Medea (Medea Destiny) Aug 2022 - Fix for issue #862, allow wearer with trusted perm to operate - +Ping (Pingout Duffield) + * Dec 2022 - Fix for issue #907, order of Welded -> Addons Limited checking et al. Licensed under the GPLv2. See LICENSE for full details. https://github.com/OpenCollarTeam/OpenCollar @@ -227,25 +228,25 @@ default } } - if(sStr == "settings=sent"){ - if(g_iAddonLimitation){ - llOwnerSay("Addons Limited is checked. To unweld, someone with Owner access (including wearer if they are Owner) must: 1) Open the collar menu. 2)Click Settings. 3) Click the Addons button in Settings. UNCHECK AddOns Limited. Leave WearerAdd and Addons CHECKED. The Wearer can then proceed to unweld."); - Link("offline", 0, "", g_kUser); - llSleep(2); - llResetScript(); - } else { - llOwnerSay("Checking for an existing collar weld"); + if(sStr == "settings=sent"){ + llSay(0, "Checking for a existing collar weld"); if(g_iWelded){ - llOwnerSay("Unweld tool now ready."); - llOwnerSay("Building consent prompt"); + llSay(0, "Unweld tool now ready."); + llSay(0, "Building consent prompt"); Link("from_addon", 0, "menu "+g_sAddon, g_kUser); - llOwnerSay("If for some reason this prompt does not show up, go into your addons menu to find the Unwelder button"); + llSay(0, "If for some reason this prompt does not show up, go into your addons menu to find the Unwelder button"); }else{ - llOwnerSay("Collar is not welded. Aborting"); + llSay(0, "Collar is not welded. Aborting"); Link("offline", 0, "", g_kUser); llSleep(2); llResetScript(); } + + if(g_iAddonLimitation){ + llSay(0, "Addons Limited is checked. To unweld, a collar owner (including unowned wearer) must go to the collar Settings menu and find the AddOn Settings button. Uncheck AddOns Limited and then proceed to unweld."); + Link("offline", 0, "", g_kUser); + llSleep(2); + llResetScript(); } } }