This shows you the differences between two versions of the page.
Previous revisionNext revision | |||
— | fuss:lsl [2019/11/12 16:46] – [Ternary Operator] office | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Copyright ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 // | ||
+ | // Please see: http:// | ||
+ | // rights of fair usage, the disclaimer and warranty conditions. | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | </ | ||
+ | |||
+ | |||
+ | ====== Empty List ====== | ||
+ | |||
+ | Instead of: | ||
+ | <code lsl2> | ||
+ | if(!llGetListLength(l))) { | ||
+ | llOwnerSay(" | ||
+ | } | ||
+ | </ | ||
+ | you can use: | ||
+ | <code lsl2> | ||
+ | default | ||
+ | { | ||
+ | state_entry() | ||
+ | { | ||
+ | list l = [ 0 ]; | ||
+ | if(l) { | ||
+ | llOwnerSay(" | ||
+ | return; | ||
+ | } | ||
+ | llOwnerSay(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | For: | ||
+ | <code lsl2> | ||
+ | list l = [ 0 ]; | ||
+ | </ | ||
+ | **Output:** | ||
+ | < | ||
+ | Object: not empty | ||
+ | </ | ||
+ | For: | ||
+ | <code lsl2> | ||
+ | list l = []; | ||
+ | </ | ||
+ | **Output:** | ||
+ | < | ||
+ | Object: empty | ||
+ | </ | ||
+ | |||
+ | The same applies for: | ||
+ | |||
+ | * vectors, '' | ||
+ | * strings, the empty string is equivalent to false | ||
+ | |||
+ | However, all of them must be alone in the '' | ||
+ | |||
+ | <code lsl2> | ||
+ | list a = []; | ||
+ | if(a) { | ||
+ | // not empty | ||
+ | } | ||
+ | // empty | ||
+ | </ | ||
+ | will work as explained earlier. | ||
+ | |||
+ | But: | ||
+ | |||
+ | <code lsl2> | ||
+ | list a = []; | ||
+ | if(a && TRUE) { | ||
+ | // a not empty | ||
+ | } | ||
+ | // empty | ||
+ | </ | ||
+ | will not compile and will issue a type mismatch error. | ||
+ | |||
+ | ====== 3-Term Logical Disjunction Using Vectors ====== | ||
+ | |||
+ | If you have three flags and you wish to create a conditional based on whether at least one of them is true, consider placing them in a vector primitive and making the vector the conditional. | ||
+ | |||
+ | **Example** | ||
+ | <code lsl2> | ||
+ | vector true = < | ||
+ | vector false = < | ||
+ | |||
+ | if(true) { // Same as if(< | ||
+ | llSay(0, " | ||
+ | return; | ||
+ | } | ||
+ | llSay(0, " | ||
+ | </ | ||
+ | |||
+ | **Usage** | ||
+ | <code lsl2> | ||
+ | integer flag_a = 0; | ||
+ | integer flag_b = 1; | ||
+ | integer flag_c = 1; | ||
+ | |||
+ | if(< | ||
+ | llSay(0, " | ||
+ | return; | ||
+ | } | ||
+ | llSay(0, " | ||
+ | </ | ||
+ | |||
+ | ====== Conditional Re-entry Flipping ====== | ||
+ | |||
+ | An elegant way to alternate between '' | ||
+ | |||
+ | **Globals** | ||
+ | <code lsl2> | ||
+ | integer o = -1; | ||
+ | </ | ||
+ | **Inline usage** | ||
+ | <code lsl2> | ||
+ | timer() { | ||
+ | if(o = ~o) { | ||
+ | /* Second timer() entry, | ||
+ | return; | ||
+ | } | ||
+ | /* First timer() entry, third timer() entry, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== ASCII Progress Bar ====== | ||
+ | |||
+ | **Globals** | ||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasProgress(integer percent, integer length, list symbols) { | ||
+ | percent /= (integer)((float)100.0/ | ||
+ | string p = llList2String(symbols, | ||
+ | integer itra = 0; | ||
+ | do { | ||
+ | if(itra> | ||
+ | else p += llList2String(symbols, | ||
+ | } while(++itra< | ||
+ | return p + llList2String(symbols, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This function will return a string in this format: | ||
+ | < | ||
+ | symbols(0) + symbols(1) percent of length times + symbols(2) till 100 percent of length + symbols(3) | ||
+ | </ | ||
+ | |||
+ | In other words, given a call: | ||
+ | <code lsl2> | ||
+ | progress(50, | ||
+ | </ | ||
+ | |||
+ | it will return the string: | ||
+ | |||
+ | < | ||
+ | [##### | ||
+ | </ | ||
+ | |||
+ | This might come in handy together with '' | ||
+ | <code lsl2> | ||
+ | llSetText(" | ||
+ | </ | ||
+ | |||
+ | ====== Generate Group-Invite Links ====== | ||
+ | |||
+ | Unless using bots, the only way to invite somebody to a group is to send them a link using the < | ||
+ | |||
+ | **Inline Usage** | ||
+ | <code lsl2> | ||
+ | llInstantMessage(id, | ||
+ | "\n secondlife:/// | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ====== Generic Notecard Reader ====== | ||
+ | |||
+ | This is a generic notecard loader. In this variation, it does not set a timer to check whether the notecard has been loaded. Thus, all the notecards should have an ending newline for this system to work properly. | ||
+ | |||
+ | **Globals** | ||
+ | <code lsl2> | ||
+ | key nQuery = NULL_KEY; | ||
+ | integer nLine = 0; | ||
+ | list nList = []; | ||
+ | //pragma inline | ||
+ | string nName = "New Notecard"; | ||
+ | </ | ||
+ | **Reading** | ||
+ | <code lsl2> | ||
+ | if(llGetInventoryType(nName) != INVENTORY_NOTECARD) { | ||
+ | llSay(DEBUG_CHANNEL, | ||
+ | return; | ||
+ | } | ||
+ | nQuery = llGetNotecardLine(nName, | ||
+ | </ | ||
+ | **Dataserver** | ||
+ | <code lsl2> | ||
+ | dataserver(key id, string data) { | ||
+ | if(id != nQuery) return; | ||
+ | if(data == EOF) return; | ||
+ | if(data == "" | ||
+ | nList += data; | ||
+ | @continue; | ||
+ | nQuery = llGetNotecardLine(nName, | ||
+ | } | ||
+ | </ | ||
+ | **Extension: | ||
+ | <code lsl2> | ||
+ | if(llListFindList(nList, | ||
+ | </ | ||
+ | |||
+ | ====== Side-By-Side Dual List Enumeration ====== | ||
+ | |||
+ | Enumerates two lists side-by side, separating the elements by a separator. | ||
+ | |||
+ | **Globals** | ||
+ | <code lsl2> | ||
+ | list list_a = [ /* contents */ ]; | ||
+ | list lib_b = [ /* contents */ ]; | ||
+ | </ | ||
+ | **Inline usage** | ||
+ | <code lsl2> | ||
+ | llOwnerSay(" | ||
+ | for(itra=0; itra< | ||
+ | llOwnerSay(llList2String(list_a, | ||
+ | llSleep(llGetRegionTimeDilation()); | ||
+ | } | ||
+ | llOwnerSay(" | ||
+ | </ | ||
+ | |||
+ | ====== Map Preserving Sort using Quicksort for Strings ====== | ||
+ | |||
+ | Although '' | ||
+ | |||
+ | More precisely, in the example below, we have a map of letters to numbers. The first column represents the contents of list_a and the right column represents the contents of list_b: | ||
+ | < | ||
+ | f -> 10 | ||
+ | a -> 37 | ||
+ | d -> 1 | ||
+ | e -> 4 | ||
+ | b -> 2 | ||
+ | c -> 3 | ||
+ | z -> 1 | ||
+ | </ | ||
+ | |||
+ | and we want to sort list_a lexicographically while preserving the mapping from letters to numbers above. The expected result should be: | ||
+ | |||
+ | < | ||
+ | a -> 37 | ||
+ | b -> 2 | ||
+ | c -> 3 | ||
+ | d -> 1 | ||
+ | e -> 4 | ||
+ | f -> 10 | ||
+ | z -> 1 | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer stringComparer(string a, string b) { | ||
+ | list alph = [ " | ||
+ | " | ||
+ | return llListFindList(alph, | ||
+ | } | ||
+ | |||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasDualQuicksort(list a, list b) { | ||
+ | if(llGetListLength(a) <= 1) return a+b; | ||
+ | |||
+ | list pivot_a = llList2List(a, | ||
+ | a = llDeleteSubList(a, | ||
+ | list pivot_b = llList2List(b, | ||
+ | b = llDeleteSubList(b, | ||
+ | |||
+ | list less = []; | ||
+ | list less_b = []; | ||
+ | list more = []; | ||
+ | list more_b = []; | ||
+ | |||
+ | do { | ||
+ | if(stringComparer(llList2String(a, | ||
+ | less += llList2List(a, | ||
+ | less_b += llList2List(b, | ||
+ | jump continue; | ||
+ | } | ||
+ | more += llList2List(a, | ||
+ | more_b += llList2List(b, | ||
+ | @continue; | ||
+ | a = llDeleteSubList(a, | ||
+ | b = llDeleteSubList(b, | ||
+ | } while(llGetListLength(a)); | ||
+ | return wasDualQuicksort(less, | ||
+ | } | ||
+ | |||
+ | default | ||
+ | { | ||
+ | state_entry() | ||
+ | { | ||
+ | list list_a = [" | ||
+ | list list_b = [ 10, 37, | ||
+ | | ||
+ | llOwnerSay(" | ||
+ | llDumpList2String(wasDualQuicksort(list_a, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This holds only for a bijective map from the set defined by the elements of list_a to the set defined by the elements of list_b. The result is, as expected, a double-length list containing the elements of the first set merged with the elements of the second set: | ||
+ | |||
+ | < | ||
+ | Object: Dual - Quicksort list contains in order: a 37 b 2 c 3 d 1 e 4 f 10 z 1 | ||
+ | </ | ||
+ | |||
+ | This is a symmetry-based variation of [[secondlife: | ||
+ | |||
+ | Concerning practical applications, | ||
+ | |||
+ | ====== Map Preserving Sort using Quicksort for Integers ====== | ||
+ | |||
+ | The same as for strings, mentioned in the previous section on this page, but this time sorting integers. It will sort a map of two lists in decreasing order of the first list. | ||
+ | |||
+ | Example: | ||
+ | <code lsl2> | ||
+ | wasDualQuicksort([10, | ||
+ | </ | ||
+ | will return the list: | ||
+ | < | ||
+ | 20, | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasDualQuicksort(list a, list b) { | ||
+ | if(llGetListLength(a) <= 1) return a+b; | ||
+ | |||
+ | integer pivot_a = llList2Integer(a, | ||
+ | a = llDeleteSubList(a, | ||
+ | string pivot_b = llList2String(b, | ||
+ | b = llDeleteSubList(b, | ||
+ | | ||
+ | list less = []; | ||
+ | list less_b = []; | ||
+ | list more = []; | ||
+ | list more_b = []; | ||
+ | |||
+ | do { | ||
+ | if(llList2Integer(a, | ||
+ | less += llList2List(a, | ||
+ | less_b += llList2List(b, | ||
+ | jump continue; | ||
+ | } | ||
+ | more += llList2List(a, | ||
+ | more_b += llList2List(b, | ||
+ | @continue; | ||
+ | a = llDeleteSubList(a, | ||
+ | b = llDeleteSubList(b, | ||
+ | } while(llGetListLength(a)); | ||
+ | return wasDualQuicksort(less, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== RLV: Wearables & Attachment Lists as Strings ====== | ||
+ | |||
+ | **Local for less memory and less speed, global for more memory and more speed.** | ||
+ | <code lsl2> | ||
+ | //pragma inline | ||
+ | list CLOTHES = [ " | ||
+ | //pragma inline | ||
+ | list ATTACHMENTS = [ " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | </ | ||
+ | |||
+ | ====== RLV: Lock Attachments ====== | ||
+ | |||
+ | Place this script in an attachment to prevent it from being removed by RLV relays or outfit changes. Click the attachment and keep holding the left mouse button for a while (a rather long while) to unlock the attachment. Click the attachment again to lock it back into place. Nuked with [[secondlife: | ||
+ | |||
+ | <code lsl2> | ||
+ | integer z; | ||
+ | {if(llDetectedKey(0)!=llGetOwner())return; | ||
+ | touch_end(integer y){if(z< | ||
+ | state unlocked; | ||
+ | </ | ||
+ | |||
+ | ====== RLV: Rotate Avatar To Face Target ====== | ||
+ | |||
+ | **Variables** | ||
+ | <code lsl2> | ||
+ | vector targetpos = < /* x */, /* y */, /* z */ >; | ||
+ | </ | ||
+ | **Inline usage** | ||
+ | <code lsl2> | ||
+ | vector pointTo = targetpos - llGetPos(); | ||
+ | float angleTo = llAtan2(pointTo.x, | ||
+ | llOwnerSay(" | ||
+ | </ | ||
+ | |||
+ | ====== Key2Number: Passed to Rezzed Objects ====== | ||
+ | |||
+ | This should be used in both the rezzed object and the rezzer. | ||
+ | |||
+ | **Global** | ||
+ | <code lsl2> | ||
+ | integer Key2Number(key objKey) { | ||
+ | return -(integer)(" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This may be used anywhere in the rezzer script. | ||
+ | |||
+ | **Rezzer: Inline usage** | ||
+ | <code lsl2> | ||
+ | llRezObject(/ | ||
+ | </ | ||
+ | |||
+ | ====== Key2Number: Homing Rezzed Object ====== | ||
+ | |||
+ | This an example application which, combined with the previous supersection, | ||
+ | |||
+ | Used in the rezzed object itself. | ||
+ | |||
+ | **Rezzed object: Globals** | ||
+ | <code lsl2> | ||
+ | integer tgtID = 0; | ||
+ | key tgtKey = NULL_KEY; | ||
+ | integer nTgT = 0; | ||
+ | </ | ||
+ | |||
+ | Used in the rezzed object itself. | ||
+ | |||
+ | **Rezzed object: Inline usage** | ||
+ | <code lsl2> | ||
+ | at_target(integer tnum, vector targetpos, vector ourpos) { | ||
+ | if(tgtKey == NULL_KEY) return; | ||
+ | llSetTimerEvent(0); | ||
+ | |||
+ | /* Target has been reached. Do something. */ | ||
+ | |||
+ | } | ||
+ | timer() { | ||
+ | llSetTimerEvent(0); | ||
+ | llTargetRemove(nTgT); | ||
+ | vector pos = llList2Vector(llGetObjectDetails(tgtKey, | ||
+ | nTgT = llTarget(pos, | ||
+ | llMoveToTarget(pos, | ||
+ | llSetTimerEvent(1); | ||
+ | } | ||
+ | on_rez(integer param) { | ||
+ | tgtID = param; | ||
+ | llSensorRepeat("", | ||
+ | } | ||
+ | sensor(integer num_detected) { | ||
+ | --num_detected; | ||
+ | do { | ||
+ | tgtKey = llDetectedKey(num_detected); | ||
+ | if(Key2Number(tgtKey) == tgtID) jump target_found; | ||
+ | } while(--num_detected> | ||
+ | return; | ||
+ | @target_found; | ||
+ | llSensorRemove(); | ||
+ | vector pos = llList2Vector(llGetObjectDetails(tgtKey, | ||
+ | nTgT = llTarget(pos, | ||
+ | |||
+ | /* | ||
+ | * Object will start moving now. | ||
+ | * Add any pre-movement directives such as, | ||
+ | * llSetStatus(STATUS_PHYSICS, | ||
+ | * instead of this comment here. | ||
+ | */ | ||
+ | |||
+ | llMoveToTarget(pos, | ||
+ | llSetTimerEvent(1); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Using Sensor as a Timer ====== | ||
+ | |||
+ | Can be used as a second '' | ||
+ | |||
+ | **Starting the sensor-timer** | ||
+ | <code lsl2> | ||
+ | llSensorRepeat("", | ||
+ | </ | ||
+ | **(No) Sensor Event Handler** | ||
+ | <code lsl2> | ||
+ | no_sensor() { | ||
+ | |||
+ | /* this area will be hit 60 seconds | ||
+ | * after the (no_)sensor handlers have | ||
+ | * been installed with llSensorRepeat(). */ | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Sensor as alarm ====== | ||
+ | |||
+ | '' | ||
+ | |||
+ | **Re-schedule alarm** | ||
+ | <code lsl2> | ||
+ | llSensorRepeat("", | ||
+ | </ | ||
+ | where '' | ||
+ | |||
+ | |||
+ | **Clock - Triggered after TIME seconds** | ||
+ | <code lsl2> | ||
+ | // triggered after alarm * t seconds. | ||
+ | no_sensor() { | ||
+ | /* this area will be hit TIME seconds. */ | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Data-Passing over States Changes without Global Variables ====== | ||
+ | |||
+ | One problem with switching states is that there is no way to pass a variable from one state to the other without using a global variable. However, the '' | ||
+ | |||
+ | **Example: | ||
+ | <code lsl2> | ||
+ | default | ||
+ | { | ||
+ | state_entry() | ||
+ | { | ||
+ | llSetTimerEvent(10); | ||
+ | llResetTime(); | ||
+ | state catch; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | state catch | ||
+ | { | ||
+ | timer() { | ||
+ | llSay(0, " | ||
+ | llSetTimerEvent(0); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | **Output:** | ||
+ | |||
+ | Delayed by 10 seconds: | ||
+ | |||
+ | < | ||
+ | Object: Integer passed to me is: 10 | ||
+ | </ | ||
+ | |||
+ | ====== Move to Mobile Point between Two Points ====== | ||
+ | |||
+ | For [[secondlife: | ||
+ | |||
+ | < | ||
+ | |||
+ | d=10 | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | </ | ||
+ | |||
+ | and we want to move an object from $A$ to $B$, $10m$ on the segment $A$C. In order to do that we calculate $B$'s position: | ||
+ | |||
+ | We know that the distance '' | ||
+ | <code lsl2> | ||
+ | float dist = llVectorDist(A, | ||
+ | </ | ||
+ | Now, we know that we want to jump 10m towards point C, so in this case we want to jump 10 meters out of the entire distance: | ||
+ | <code lsl2> | ||
+ | jumpDistance = 10/dist; | ||
+ | </ | ||
+ | So, the final position is given by: | ||
+ | <code lsl2> | ||
+ | final_position = posA + jumpDistance * (posC-posA); | ||
+ | </ | ||
+ | |||
+ | A function that computes $10m$ gates between two points is '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list jumpGates(vector iPos, vector dPos, integer jumpDistance) { | ||
+ | list gates = []; | ||
+ | if(jumpDistance == 0) return gates; | ||
+ | float dist = llVecDist(iPos, | ||
+ | if(dist > jumpDistance) { | ||
+ | iPos = iPos + jumpDistance * (dPos-iPos) / dist; | ||
+ | gates += iPos; | ||
+ | return gates + jumpGates(iPos, | ||
+ | } | ||
+ | return gates + jumpGates(iPos, | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | ====== Planar or Grid Distance ====== | ||
+ | |||
+ | {{ fuss_lsl_planar_distance.png? | ||
+ | |||
+ | The $x,y$ planar distance between the kitty and the book is given by: | ||
+ | <code lsl2> | ||
+ | float length_of_red_primitive=llVecDist(< | ||
+ | </ | ||
+ | |||
+ | This works for all planes: $(x,y)$, $(x,z)$ and $(y,z)$ by eliminating one of the components - in this example, $z$. | ||
+ | |||
+ | ====== Object Rotate to Face Object ====== | ||
+ | |||
+ | {{ fuss_lsl_kitty_faces_book.png? | ||
+ | |||
+ | The rotation is given by $q\Delta$. | ||
+ | <code lsl2> | ||
+ | vector book_position = < | ||
+ | vector kitty_position = < | ||
+ | |||
+ | default { | ||
+ | state_entry() { | ||
+ | rotation qΔ = llRotBetween(< | ||
+ | book_position.y-kitty_position.y, | ||
+ | book_position.z-kitty_position.z> | ||
+ | llSetRot(llGetRot() * qΔ); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | By eliminating a component $x$, $y$ or $z$ in '' | ||
+ | |||
+ | ====== Propulse an Object towards Target (Artillery) ====== | ||
+ | |||
+ | This will hurl an object at a target using, in order: | ||
+ | |||
+ | - High angle. | ||
+ | - Low angle. | ||
+ | - Abort if object won't even lift off. | ||
+ | |||
+ | More explanations on the [[secondlife: | ||
+ | |||
+ | **Globals** | ||
+ | <code lsl2> | ||
+ | vector target = < /* x */, /* y */, /* z */ >; | ||
+ | float velocity = /* some velocity */; | ||
+ | </ | ||
+ | **Inline usage** | ||
+ | <code lsl2> | ||
+ | vector origin = llGetPos(); | ||
+ | dΔ=llVecDist(< | ||
+ | valSin = 9.81*dΔ/ | ||
+ | if(valSin < -1 || valSin > 1) valSin = 9.81/ | ||
+ | if(valSin < -1 || valSin > 1) return; /* Won't even lift the projectile off the ground. Abort. */ | ||
+ | llSetVelocity(llVecNorm(< | ||
+ | dΔ*llTan((90-RAD_TO_DEG*llAsin(valSin)/ | ||
+ | | ||
+ | </ | ||
+ | |||
+ | ====== Pass Data to a Rezzed Object ====== | ||
+ | |||
+ | This will allow you to avoid using '' | ||
+ | |||
+ | **Hasher** | ||
+ | <code lsl2> | ||
+ | integer Key2Number(key objKey) { | ||
+ | return -(integer)(" | ||
+ | } | ||
+ | </ | ||
+ | **Inline usage** | ||
+ | <code lsl2> | ||
+ | llRezObject(" | ||
+ | </ | ||
+ | **Rezzed object event** | ||
+ | <code lsl2> | ||
+ | default | ||
+ | { | ||
+ | on_rez(integer param) { | ||
+ | /* param contains the passed data */ | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Persistent Storage ====== | ||
+ | |||
+ | {{indexmenu> | ||
+ | |||
+ | |||
+ | ====== Fast Texture Caching ====== | ||
+ | |||
+ | This is a texture cacher that quickly loads some textures on a 10-faced hidden primitive and is useful for beamers, talks, picture frames and other builds where the texture loading time can be annoying. It does that by permuting textures on each face, thereby forcing viewers in range to keep the texture alive. | ||
+ | |||
+ | * To preload a bunch of textures: | ||
+ | <code lsl2> | ||
+ | llMessageLinked(/ | ||
+ | </ | ||
+ | where the string of textures separated by commas could be something like: " | ||
+ | |||
+ | * When the fast texture cacher obtained the data above, it sends a confirmation using: | ||
+ | <code lsl2> | ||
+ | llMessageLinked(sender, | ||
+ | </ | ||
+ | where sender is the primitive that sent the caching request in the first place. | ||
+ | |||
+ | **Full script:** | ||
+ | <file lsl2 ftc.lsl> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | |||
+ | list slides = []; | ||
+ | integer sides = 0; | ||
+ | |||
+ | default { | ||
+ | state_entry() { | ||
+ | // Thanks for the music Salahzar Stenvaag, 10 faces is cool! | ||
+ | llSetPrimitiveParams([PRIM_TYPE, | ||
+ | <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, PRIM_SIZE, <0.03, 2.89, 0.5>, PRIM_COLOR, ALL_SIDES, < | ||
+ | sides = llGetNumberOfSides(); | ||
+ | } | ||
+ | | ||
+ | timer() { | ||
+ | string s = llList2String(slides, | ||
+ | llSetTexture(s, | ||
+ | slides = llDeleteSubList(slides, | ||
+ | slides += s; | ||
+ | if(--sides == 0) sides = llGetNumberOfSides(); | ||
+ | } | ||
+ | | ||
+ | link_message(integer sender, integer num, string message, key id) { | ||
+ | if(message != " | ||
+ | llSetTimerEvent(0); | ||
+ | slides = llCSV2List(id); | ||
+ | llMessageLinked(sender, | ||
+ | llSetTimerEvent(1.01-llGetRegionTimeDilation()); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Time Dilation Normed to 10 ====== | ||
+ | |||
+ | A short snippet that sets the primitive' | ||
+ | |||
+ | < | ||
+ | dilation_norm_10 = (1.0 - dilation) * 10 | ||
+ | </ | ||
+ | |||
+ | **Full script:** | ||
+ | <code lsl2> | ||
+ | default | ||
+ | { | ||
+ | state_entry() { | ||
+ | llSetTimerEvent(1); | ||
+ | } | ||
+ | |||
+ | timer() { | ||
+ | llSetText(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Fast Integer Exponentiation ====== | ||
+ | |||
+ | Works for integers, ex: place a call with | ||
+ | <code lsl2> | ||
+ | fsPow(2, 2) | ||
+ | </ | ||
+ | for the function to return the value of 2^2. | ||
+ | |||
+ | <code lsl2> | ||
+ | integer fsPow(integer base, integer exponent) { | ||
+ | integer result = 1; | ||
+ | do { | ||
+ | if(exponent & 1) result *= base; | ||
+ | exponent = exponent >> 1; | ||
+ | base *= base; | ||
+ | } while(exponent); | ||
+ | return result; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Xorshift ====== | ||
+ | |||
+ | Fast random number generator, invented by [[http:// | ||
+ | |||
+ | <code lsl2> | ||
+ | integer x = 123456789; | ||
+ | integer y = 362436069; | ||
+ | integer z = 521288629; | ||
+ | integer w = 88675123; | ||
+ | | ||
+ | integer xor128() { | ||
+ | integer t; | ||
+ | t = x ^ (x << 11); | ||
+ | x = y; y = z; z = w; | ||
+ | return w = w ^ (w >> 19) ^ (t ^ (t >> 8)); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== TRNG ====== | ||
+ | |||
+ | True random-number generator, based on FPS fluctuations of region frame-rates as discussed on the page at [[drafts: | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Requires: a limit. | ||
+ | // Provides: true random number between [0, max) or (-max, 0]. | ||
+ | ////////////////////////////////////////////////////////// | ||
+ | integer wasFPSrand(integer max) { | ||
+ | integer r = (integer)(llGetRegionFPS() * 10000000.0) % max; | ||
+ | if(max > 0) return r; else return -r; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Element Selection Based on Weighted Probabilities ====== | ||
+ | |||
+ | A cumulative algorithm that may be used to select an element from a list, where each element is weighted with different probabilities. | ||
+ | |||
+ | For example, selecting an element from the list below based on the corresponding probabilities: | ||
+ | < | ||
+ | A -> 60% | ||
+ | B -> 10% | ||
+ | C -> 10% | ||
+ | D -> 20% | ||
+ | </ | ||
+ | |||
+ | Can be done using the cumulative algorithm below. The dualQuicksort can be obtained from this page, in a previous section and is there because in certain circumstances one may calculate the probabilities for A, B, C and D dynamically without any previous knowledge. | ||
+ | |||
+ | <code lsl2> | ||
+ | list sorted = dualQuicksort([ 60, 10, 10, 20 ], [ " | ||
+ | list identifier_list = llList2ListStrided(llDeleteSubList(sorted, | ||
+ | list identifier_probabilities = llList2ListStrided(sorted, | ||
+ | float rnd = llFrand(1); | ||
+ | float cum = 0; | ||
+ | string select = ""; | ||
+ | integer itra = llGetListLength(alleles); | ||
+ | do { | ||
+ | cum += llList2Float(identifier_probabilities, | ||
+ | if(cum >= rnd) { | ||
+ | select = llList2String(identifier_list, | ||
+ | jump draw; | ||
+ | } | ||
+ | } while(--itra> | ||
+ | @draw; | ||
+ | // the variable select now contains the identifier A, B, C or D | ||
+ | // based on the probabilities mentioned above. | ||
+ | </ | ||
+ | |||
+ | ====== Compute Capping System Delays ====== | ||
+ | |||
+ | A calculator to compute the random amount of time needed (in seconds) to wait between executing some capped action as discussed in our article [[drafts: | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Requires: a cap limit and an initial pad, usually 0. | ||
+ | // Provides: a random interval which must be multiplied by | ||
+ | // number of queued actions. | ||
+ | ////////////////////////////////////////////////////////// | ||
+ | float minCapDodgeTimeF(float cap, float pad) { | ||
+ | if(cap < 0.1) return pad; | ||
+ | pad += cap / 2.0; | ||
+ | cap /= 10.0; | ||
+ | return minCapDodgeTimeF(cap, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== wasSpaceWrap ====== | ||
+ | |||
+ | This function wraps a string '' | ||
+ | |||
+ | Example, suppose column is '' | ||
+ | < | ||
+ | Marry had a | ||
+ | little lamb, | ||
+ | Its fleece | ||
+ | was white | ||
+ | as snow; And | ||
+ | everywhere | ||
+ | that Mary | ||
+ | went, The | ||
+ | lamb was sure | ||
+ | to go. | ||
+ | </ | ||
+ | |||
+ | **Global** | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Requires: a string txt, a delimiter, a column number | ||
+ | // Provides: a string split at the first space after column | ||
+ | ////////////////////////////////////////////////////////// | ||
+ | string wasSpaceWrap(string txt, string delimiter, integer column) { | ||
+ | string ret = llGetSubString(txt, | ||
+ | integer len = llStringLength(txt); | ||
+ | integer itra=1; | ||
+ | integer itrb=1; | ||
+ | do { | ||
+ | if(itrb % column == 0) { | ||
+ | while(llGetSubString(txt, | ||
+ | ret += llGetSubString(txt, | ||
+ | if(++itra> | ||
+ | } | ||
+ | ret += delimiter; | ||
+ | itrb = 1; | ||
+ | jump next; | ||
+ | } | ||
+ | ret += llGetSubString(txt, | ||
+ | ++itrb; | ||
+ | @next; | ||
+ | } while(++itra< | ||
+ | return ret; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Rename Primitives in a Linked Set ====== | ||
+ | |||
+ | Uses the new '' | ||
+ | |||
+ | <code lsl2> | ||
+ | default{touch_start(integer z){integer x=-(integer)llFrand(10); | ||
+ | llGetOwner()," | ||
+ | string u){integer t=llGetNumberOfPrims(); | ||
+ | while(--t> | ||
+ | </ | ||
+ | |||
+ | ====== String Reverse ====== | ||
+ | |||
+ | Recursive string reversal; sweet solution from [[http:// | ||
+ | |||
+ | **Global** | ||
+ | |||
+ | <code lsl2> | ||
+ | string wasStringReverse(string str) { | ||
+ | if(llStringLength(str)< | ||
+ | return wasStringReverse(llGetSubString(str, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | **Example** | ||
+ | <code lsl2> | ||
+ | llOwnerSay(wasStringReverse(" | ||
+ | </ | ||
+ | |||
+ | **Output** | ||
+ | < | ||
+ | Object: !םלוע ,םולש | ||
+ | </ | ||
+ | |||
+ | ====== List Reverse ====== | ||
+ | |||
+ | Same as above but using lists instead of strings. | ||
+ | |||
+ | **Global** | ||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasListReverse(list lst) { | ||
+ | if (llGetListLength(lst) <= 1) return lst; | ||
+ | return wasListReverse( | ||
+ | llList2List( | ||
+ | lst, | ||
+ | 1, | ||
+ | llGetListLength(lst) | ||
+ | ) | ||
+ | ) + llList2List(lst, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | **Example** | ||
+ | <code lsl2> | ||
+ | list CLOTHES = [ " | ||
+ | llOwnerSay(llDumpList2String(wasListReverse(CLOTHES), | ||
+ | </ | ||
+ | |||
+ | **Output** | ||
+ | < | ||
+ | shape hair eyes skin undershirt underpants socks skirt shoes shirt pants jacket gloves | ||
+ | </ | ||
+ | |||
+ | ====== XML Stream Parser ====== | ||
+ | |||
+ | This is a StAX stream parser which is able to read '' | ||
+ | ===== Get Node Value ===== | ||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasStAX_GetNodeValue(string xmlStream, string node) { | ||
+ | list stream = llParseString2List(xmlStream, | ||
+ | integer size = llGetListLength(stream); | ||
+ | string value = ""; | ||
+ | integer ptr = 0; | ||
+ | do { | ||
+ | string current = llList2String(stream, | ||
+ | string lookback = llList2String(stream, | ||
+ | |||
+ | if(current != "/" | ||
+ | StAX += current; | ||
+ | jump next_tag; | ||
+ | } | ||
+ | if(lookback == "/" | ||
+ | StAX = llDeleteSubList(StAX, | ||
+ | jump next_tag; | ||
+ | } | ||
+ | if(current != ">" | ||
+ | if(llList2String(StAX, | ||
+ | value += current + " "; | ||
+ | @next_tag; | ||
+ | } while(++ptr< | ||
+ | | ||
+ | if(llGetListLength(StAX) != 0) { | ||
+ | llSay(DEBUG_CHANNEL, | ||
+ | } | ||
+ | return value; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Set Node Value ===== | ||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasStAX_SetNodeValue(string xmlStream, string node, string value) { | ||
+ | list stream = llParseString2List(xmlStream, | ||
+ | integer size = llGetListLength(stream); | ||
+ | list StAX = []; | ||
+ | integer ptr = 0; | ||
+ | integer set = 0; | ||
+ | do { | ||
+ | string current = llList2String(stream, | ||
+ | string lookback = llList2String(stream, | ||
+ | |||
+ | if(current != "/" | ||
+ | StAX += current; | ||
+ | jump next_tag; | ||
+ | } | ||
+ | if(lookback == "/" | ||
+ | StAX = llDeleteSubList(StAX, | ||
+ | jump next_tag; | ||
+ | } | ||
+ | if(current != ">" | ||
+ | if(llList2String(StAX, | ||
+ | if(!set) { | ||
+ | stream = llListReplaceList(stream, | ||
+ | set = 1; | ||
+ | jump next_tag; | ||
+ | } | ||
+ | stream = llListReplaceList(stream, | ||
+ | } | ||
+ | @next_tag; | ||
+ | } while(++ptr< | ||
+ | |||
+ | if(llGetListLength(StAX) != 0) { | ||
+ | llSay(DEBUG_CHANNEL, | ||
+ | } | ||
+ | return llDumpList2String(stream, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== wasBioLuminescence ====== | ||
+ | |||
+ | Can be called every hour to make lagoons glow and simulate oceans or seas. | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | float wasBioLuminescence(float hours) { | ||
+ | float sin = llSin(0.1*hours); | ||
+ | if(sin> | ||
+ | return sin; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ====== Two in One Integers ====== | ||
+ | |||
+ | Two integers '' | ||
+ | |||
+ | Pack: | ||
+ | <code lsl2> | ||
+ | var_c = var_a << 16 | var_b | ||
+ | </ | ||
+ | |||
+ | Unpack: | ||
+ | <code lsl2> | ||
+ | var_a = var_c >> 16; | ||
+ | var_b = var_c & 0xFFFF; | ||
+ | </ | ||
+ | |||
+ | ====== Permute Sort ====== | ||
+ | |||
+ | Permute sort, uses a reference string to sort an input string. | ||
+ | |||
+ | Example reference: | ||
+ | < | ||
+ | rraaats | ||
+ | </ | ||
+ | example input: | ||
+ | < | ||
+ | taaas | ||
+ | </ | ||
+ | example output: | ||
+ | < | ||
+ | aaats | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | ///////////////////////////////////////////// | ||
+ | // [K] Permute Sort is an algorithm to // | ||
+ | // sort two strings depending on the // | ||
+ | // relative positions of their elements. | ||
+ | ///////////////////////////////////////////// | ||
+ | string permuteSort(string input, string refer) { | ||
+ | |||
+ | integer itra = llStringLength(refer)-1; | ||
+ | do { | ||
+ | string s = llGetSubString(refer, | ||
+ | if(llSubStringIndex(input, | ||
+ | refer = llDeleteSubString(refer, | ||
+ | } | ||
+ | } while(--itra> | ||
+ | return refer; | ||
+ | | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Float Limits ====== | ||
+ | |||
+ | ^ Minimum Value ^ Maximum Value ^ Cast Infinite (Dangerous for LSO) ^ | ||
+ | | '' | ||
+ | |||
+ | ====== Subtract Value from Value List ====== | ||
+ | Example input, '' | ||
+ | < | ||
+ | 2 3 8 2 4 6 | ||
+ | </ | ||
+ | and '' | ||
+ | |||
+ | Result: | ||
+ | < | ||
+ | 1 2 7 1 3 5 | ||
+ | </ | ||
+ | |||
+ | Returns '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasListSubtract(list input, integer value) { | ||
+ | integer v = llList2Integer(input, | ||
+ | input = llDeleteSubList(input, | ||
+ | if(input == []) return [v-value] + input; | ||
+ | return [v-value] + wasListSubtract(input, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Add-up all List Elements ====== | ||
+ | |||
+ | The following recursive function can be used instead of '' | ||
+ | |||
+ | Example input: | ||
+ | < | ||
+ | 2 3 8 2 4 6 | ||
+ | </ | ||
+ | Example output: | ||
+ | < | ||
+ | 25 | ||
+ | </ | ||
+ | |||
+ | Returns '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasAddListElements(list input) { | ||
+ | if(input == []) return 0; | ||
+ | return llList2Integer(input, | ||
+ | wasAddListElements(llDeleteSubList(input, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Remove Multiple Elements From List ====== | ||
+ | |||
+ | Input: | ||
+ | <code lsl2> | ||
+ | wasSubtractSubList([" | ||
+ | </ | ||
+ | |||
+ | Output: | ||
+ | < | ||
+ | a b | ||
+ | </ | ||
+ | |||
+ | Input: | ||
+ | <code lsl2> | ||
+ | wasSubtractSubList([" | ||
+ | </ | ||
+ | |||
+ | Output: | ||
+ | < | ||
+ | a c | ||
+ | </ | ||
+ | |||
+ | Input: | ||
+ | <code lsl2> | ||
+ | wasSubtractSubList([" | ||
+ | </ | ||
+ | |||
+ | Output: | ||
+ | < | ||
+ | c d | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasSubtractSubList(list input, list delete) { | ||
+ | do { | ||
+ | string tok = llList2String(delete, | ||
+ | list clean = input; | ||
+ | do { | ||
+ | if(llList2String(clean, | ||
+ | integer idx = llListFindList(input, | ||
+ | input = llDeleteSubList(input, | ||
+ | @skip; | ||
+ | clean = llDeleteSubList(clean, | ||
+ | } while(llGetListLength(clean)); | ||
+ | delete = llDeleteSubList(delete, | ||
+ | } while(llGetListLength(delete)); | ||
+ | return input; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Generate UUID v4 ====== | ||
+ | |||
+ | Input: | ||
+ | <code lsl2> | ||
+ | key gen = wasUUIDgen(234234); | ||
+ | if(gen) { | ||
+ | llOwnerSay(" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Output: | ||
+ | < | ||
+ | Valid key: e5259984-f745-4398-a5f7-2317cc724d9a | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | key wasUUIDgen(integer seed) { | ||
+ | // UUIDv4 | ||
+ | // xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx | ||
+ | // x - hex | ||
+ | // y - 8, 9, A or B | ||
+ | string result = ""; | ||
+ | integer itra = 0; | ||
+ | do { | ||
+ | string n = ""; | ||
+ | if(itra == 8 || itra == 13 || itra == 18 || itra == 23) { | ||
+ | n = " | ||
+ | jump append; | ||
+ | } | ||
+ | if(itra == 14) { | ||
+ | n = " | ||
+ | jump append; | ||
+ | } | ||
+ | if(itra == 19) { | ||
+ | n = llList2String([" | ||
+ | jump append; | ||
+ | } | ||
+ | n = llList2String([" | ||
+ | @append; | ||
+ | result += n; | ||
+ | } while(++itra< | ||
+ | return (key)result; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Levenshtein Distance ====== | ||
+ | |||
+ | The Levenshtein distance is equal to the number of single-character edits required to change one word into the other. | ||
+ | |||
+ | Input: | ||
+ | <code lsl2> | ||
+ | wasLevenshteinDistance(" | ||
+ | </ | ||
+ | |||
+ | Return: | ||
+ | < | ||
+ | 3 | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasLevenshteinDistance(string a, string b) { | ||
+ | if(a == b) return 0; | ||
+ | | ||
+ | integer sa = llStringLength(a); | ||
+ | integer sb = llStringLength(b); | ||
+ | | ||
+ | if(sa == 0) | ||
+ | return sb; | ||
+ | if(sb == 0) | ||
+ | return sa; | ||
+ | | ||
+ | list v0 = []; | ||
+ | list v1 = []; | ||
+ | | ||
+ | integer i = 0; | ||
+ | do { | ||
+ | v0 += i; | ||
+ | } while(++i < sb + 1); | ||
+ | | ||
+ | i = 0; | ||
+ | do { | ||
+ | v1 += 0; | ||
+ | } while(++i < sb + 1); | ||
+ | | ||
+ | i = 0; | ||
+ | do { | ||
+ | | ||
+ | v1 = llListReplaceList(v1, | ||
+ | |||
+ | integer j = 0; | ||
+ | do { | ||
+ | integer cost = 0; | ||
+ | if(llGetSubString(a, | ||
+ | cost = 1; | ||
+ | integer x = llList2Integer(v1, | ||
+ | integer y = llList2Integer(v0, | ||
+ | integer z = llList2Integer(v0, | ||
+ | v1 = llListReplaceList(v1, | ||
+ | (list) ( | ||
+ | llAbs((llAbs(x >= y) * y) > z) * z + | ||
+ | llAbs((llAbs(y > x) * x) >= z) * z + | ||
+ | llAbs((llAbs(z >= x) * x) > y) * y + | ||
+ | llAbs((llAbs(x > z) * z) >= y) * y + | ||
+ | llAbs((llAbs(y >= z) * z) > x) * x + | ||
+ | llAbs((llAbs(z > y) * y) >= x) * x | | ||
+ | llAbs((llAbs(x == y) * x) == z) * x | ||
+ | ), | ||
+ | j + 1, | ||
+ | j + 1 | ||
+ | ); | ||
+ | } while(++j < sb); | ||
+ | | ||
+ | j = 0; | ||
+ | do { | ||
+ | v0 = llListReplaceList( | ||
+ | v0, | ||
+ | (list)llList2String( | ||
+ | v1, | ||
+ | j | ||
+ | ), | ||
+ | j, | ||
+ | j | ||
+ | ); | ||
+ | } while(++j < sb); | ||
+ | | ||
+ | } while(++i < sa); | ||
+ | | ||
+ | return llList2Integer(v1, | ||
+ | } | ||
+ | </ | ||
+ | ====== Minimal and Maximal Value in List ====== | ||
+ | |||
+ | This is a fast recursive alternative to using '' | ||
+ | |||
+ | Input: | ||
+ | <code lsl2> | ||
+ | wasMinimum([10, | ||
+ | </ | ||
+ | |||
+ | Return: | ||
+ | < | ||
+ | 1 | ||
+ | </ | ||
+ | |||
+ | ===== Minimal Value ===== | ||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasMinimum(list integers) { | ||
+ | if(llGetListLength(integers) == 1) { | ||
+ | return llList2Integer(integers, | ||
+ | } | ||
+ | integer i = llList2Integer(integers, | ||
+ | integer j = llList2Integer(integers, | ||
+ | if(j < i) { | ||
+ | integers = llDeleteSubList(integers, | ||
+ | integers = llListReplaceList(integers, | ||
+ | } | ||
+ | integers = llDeleteSubList(integers, | ||
+ | return wasMinimum(integers); | ||
+ | } | ||
+ | </ | ||
+ | ===== Maximal Value ===== | ||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasMaximum(list integers) { | ||
+ | if(llGetListLength(integers) == 1) { | ||
+ | return llList2Integer(integers, | ||
+ | } | ||
+ | integer i = llList2Integer(integers, | ||
+ | integer j = llList2Integer(integers, | ||
+ | if(j > i) { | ||
+ | integers = llDeleteSubList(integers, | ||
+ | integers = llListReplaceList(integers, | ||
+ | } | ||
+ | integers = llDeleteSubList(integers, | ||
+ | return wasMaximum(integers); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Reasoning ===== | ||
+ | |||
+ | The reasoning is based on using lists as queues: | ||
+ | |||
+ | - [Base Case] **If** the queue contains just one element, **return** that element. | ||
+ | - **If** the second element in the queue is smaller than the first element in the queue: | ||
+ | - Delete the first element in the queue and replace it with the second element in the queue. | ||
+ | - Delete the second element in the queue. | ||
+ | - **return** queue of (1). | ||
+ | |||
+ | The only difference between '' | ||
+ | |||
+ | <code lsl2> | ||
+ | if(j < i) { | ||
+ | </ | ||
+ | |||
+ | for finding the minimal value, and | ||
+ | |||
+ | <code lsl2> | ||
+ | if(j > i) { | ||
+ | </ | ||
+ | |||
+ | for finding the maximal value. | ||
+ | |||
+ | The algorithm is not limited to integers and will work for floats as well. | ||
+ | |||
+ | ====== Find Unique Elements in List ====== | ||
+ | |||
+ | '' | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | The choice of the '' | ||
+ | |||
+ | * When the list is not already sorted (the '' | ||
+ | * When the list is already sorted (the '' | ||
+ | |||
+ | Call: | ||
+ | <code lsl2> | ||
+ | wasListUnique([1, | ||
+ | </ | ||
+ | |||
+ | Return: | ||
+ | < | ||
+ | [1, 2, 3, 5, 7, 8] | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasListUnique(list in, integer sorted) { | ||
+ | if(llGetListLength(in) == 0) return []; | ||
+ | if(sorted == TRUE) jump sorted; | ||
+ | in = llListSort(in, | ||
+ | @sorted; | ||
+ | string a = llList2String(in, | ||
+ | string b = llList2String(in, | ||
+ | list result = []; | ||
+ | if(a == b) jump dupe; | ||
+ | result += a; | ||
+ | @dupe; | ||
+ | return result + wasListUnique(llDeleteSubList(in, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Depending on the data-types contained in the input list '' | ||
+ | |||
+ | * In case the input list '' | ||
+ | * In case the input list '' | ||
+ | * In case the input list '' | ||
+ | |||
+ | ====== Delete Sub List Elements Matching a String ====== | ||
+ | |||
+ | The function takes two parameters: | ||
+ | |||
+ | * an input list '' | ||
+ | * a string '' | ||
+ | |||
+ | and returns the elements in list '' | ||
+ | |||
+ | Call: | ||
+ | <code lsl2> | ||
+ | wasDeleteSubListMatch([" | ||
+ | </ | ||
+ | |||
+ | Return: | ||
+ | < | ||
+ | [ " | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasDeleteSubListMatch(list in, string match) { | ||
+ | if(llGetListLength(in) == 0) return []; | ||
+ | string first = llList2String(in, | ||
+ | in = llDeleteSubList(in, | ||
+ | if(llSubStringIndex(first, | ||
+ | return wasDeleteSubListMatch(in, | ||
+ | @next; | ||
+ | return first + wasDeleteSubListMatch(in, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Number to Roman Numeral ====== | ||
+ | |||
+ | Input: | ||
+ | < | ||
+ | 23 | ||
+ | </ | ||
+ | |||
+ | Output: | ||
+ | < | ||
+ | XXIII | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasNumberToRoman(integer num) { | ||
+ | list n = [ 1000, 900, 500, 400, 100,90, 50, 40, 10, 9, 5, 4, 1 ]; | ||
+ | list r = [ " | ||
+ | integer i = 0; | ||
+ | string result = ""; | ||
+ | do { | ||
+ | while(num >= llList2Integer(n, | ||
+ | num -= llList2Integer(n, | ||
+ | result += llList2String(r, | ||
+ | } | ||
+ | } while(++i < llGetListLength(n)); | ||
+ | return result; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Pretty Numbers ====== | ||
+ | |||
+ | Call: | ||
+ | <code lsl2> | ||
+ | wasPrettyNumbers(15, | ||
+ | </ | ||
+ | |||
+ | Return: | ||
+ | <code lsl2> | ||
+ | ⓵⓹ | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasPrettyNumbers(integer num, list modNine) { | ||
+ | list d = llParseString2List((string)num, | ||
+ | if(llGetListLength(d) == 0) return llList2String(modNine, | ||
+ | string r = ""; | ||
+ | do { | ||
+ | r += llList2String(modNine, | ||
+ | d = llDeleteSubList(d, | ||
+ | } while(llGetListLength(d)); | ||
+ | return r; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Permute a Set ====== | ||
+ | |||
+ | Returns a list containing all the permutations of a set. | ||
+ | |||
+ | A call such as: | ||
+ | <code lsl2> | ||
+ | wasPermuteSet([" | ||
+ | </ | ||
+ | where '' | ||
+ | <code lsl2> | ||
+ | a b b a | ||
+ | </ | ||
+ | |||
+ | '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // swaps two elements in a list | ||
+ | list wasListSwap(list set, integer a, integer b) { | ||
+ | list t = llList2List(set, | ||
+ | set = llListReplaceList(set, | ||
+ | set = llListReplaceList(set, | ||
+ | return set; | ||
+ | } | ||
+ | |||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // retuns a set that contains all the permutations from the given set | ||
+ | list wasPermuteSet(list set, integer start, integer end) { | ||
+ | list result = []; | ||
+ | if(start == end) return set; | ||
+ | integer i = start; | ||
+ | do { | ||
+ | set = wasListSwap(set, | ||
+ | result += wasPermuteSet(set, | ||
+ | set = wasListSwap(set, | ||
+ | } while(++i< | ||
+ | return result; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Calculate Factorial ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Calculates n! | ||
+ | integer wasFactorial(integer n) { | ||
+ | if(n == 1) return 1; | ||
+ | return wasFactorial(--n) * n; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Insertion Sort ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasInsertSort(list toSort) { | ||
+ | list sorted = []; | ||
+ | @next_element; | ||
+ | integer n = llList2Integer(toSort, | ||
+ | toSort = llDeleteSubList(toSort, | ||
+ | integer s = llGetListLength(sorted)-1; | ||
+ | if(s == -1) { | ||
+ | sorted += n; | ||
+ | jump next_element; | ||
+ | } | ||
+ | do { | ||
+ | integer sn = llList2Integer(sorted, | ||
+ | if(n < sn) jump continue; | ||
+ | sorted = llListInsertList(sorted, | ||
+ | if(llGetListLength(toSort)) jump next_element; | ||
+ | return sorted; | ||
+ | @continue; | ||
+ | } while(--s> | ||
+ | sorted = llListInsertList(sorted, | ||
+ | if(llGetListLength(toSort)) jump next_element; | ||
+ | return sorted; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Remove Bling and Particle Effects ====== | ||
+ | |||
+ | Copy and paste into any script inside a primitive to remove the bling and any other particle effects. | ||
+ | <code lsl2> | ||
+ | default{state_entry(){llParticleSystem([]); | ||
+ | </ | ||
+ | |||
+ | ====== Remove Texture Animation ====== | ||
+ | |||
+ | Copy and paste into any script inside a primitive to stop all texture animations. | ||
+ | <code lsl2> | ||
+ | default{state_entry(){llSetTextureAnim(0, | ||
+ | </ | ||
+ | |||
+ | ====== Percent to Gradient ====== | ||
+ | |||
+ | A color in LSL is represented by a vector where the $x$, $y$ and $z$ components represent the red, green and blue colors respectively. Given a gradient path between two colors, the following function will return a vector that represents a percentage of that path. The function can be used to create nice gradients, such as the ones used for the health meters in [[secondlife: | ||
+ | |||
+ | For example, a call such as: | ||
+ | <code lsl2> | ||
+ | wasPercentToGradient(50, | ||
+ | </ | ||
+ | will return the color vector ''< | ||
+ | |||
+ | The '' | ||
+ | ^ '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | | '' | ||
+ | |||
+ | The function does not convert from HSV, instead it eliminates the saturation problem by doubling the output vector and benefits from the fact that color vectors in LSL have their components scaled to '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | vector wasPercentToGradient(float percent, string rgb) { | ||
+ | if(llStringLength(rgb) != 2) { | ||
+ | llSay(DEBUG_CHANNEL, | ||
+ | return ZERO_VECTOR; | ||
+ | } | ||
+ | string a = llGetSubString(rgb, | ||
+ | string b = llGetSubString(rgb, | ||
+ | list col = [ " | ||
+ | integer ax = llListFindList(col, | ||
+ | integer bx = llListFindList(col, | ||
+ | if(ax == -1 || bx == -1) { | ||
+ | llSay(DEBUG_CHANNEL, | ||
+ | return ZERO_VECTOR; | ||
+ | } | ||
+ | col = llListReplaceList(col, | ||
+ | col = llListReplaceList(col, | ||
+ | return 2*< | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Percent to Brightness Gradient ====== | ||
+ | |||
+ | When '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | vector wasPercentToGradientBrighness(float percent, vector from, vector to) { | ||
+ | return <from.x + (to.x-from.x)*percent/ | ||
+ | from.y + (to.y-from.y)*percent/ | ||
+ | from.z + (to.z-from.z)*percent/ | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Shuffle Lists ====== | ||
+ | |||
+ | Some remarks: | ||
+ | |||
+ | * if we were to calculate the [[fuss: | ||
+ | * on the other hand, the [[fuss: | ||
+ | |||
+ | ===== Knuth Shuffle ======== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasKnuthShuffle(list data) { | ||
+ | integer i=llGetListLength(data)-1; | ||
+ | do { | ||
+ | string a = llList2String(data, | ||
+ | integer rnd = (integer)llFrand(i); | ||
+ | string b = llList2String(data, | ||
+ | data = llListReplaceList(data, | ||
+ | data = llListReplaceList(data, | ||
+ | } while(--i> | ||
+ | return data; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Shuffle List Randomly ===== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasShuffleList(list data) { | ||
+ | if(data == []) return []; | ||
+ | integer rnd = (integer)llFrand(llGetListLength(data)); | ||
+ | return [ llList2String(data, | ||
+ | wasShuffleList(llDeleteSubList(data, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Binary Search ====== | ||
+ | |||
+ | Binary search expects the '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | float wasBinarySearch(list data, float item) { | ||
+ | // assert(data is sorted) | ||
+ | if(data == []) return -1; | ||
+ | integer l = llGetListLength(data); | ||
+ | integer m = l/2; | ||
+ | float mItem = llList2Float(data, | ||
+ | if(mItem == item) return mItem; | ||
+ | if(mItem < item) { | ||
+ | data = llDeleteSubList(data, | ||
+ | jump continue; | ||
+ | } | ||
+ | data = llDeleteSubList(data, | ||
+ | @continue; | ||
+ | return wasBinarySearch2(data, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | For a complete binary-tree and binary search tree (BST) implementation in LSL see the [[secondlife: | ||
+ | ===== Notes ===== | ||
+ | |||
+ | [[fuss: | ||
+ | |||
+ | <code bash> | ||
+ | jot - 1 1000 | ||
+ | </ | ||
+ | |||
+ | The goal was to find the element '' | ||
+ | |||
+ | < | ||
+ | Object: Binary search, found: 53.000000 | ||
+ | Object: Binary search took: 0.004547 seconds | ||
+ | Object: llListFindList found 53 at index: 52 | ||
+ | Object: llListFindList took: 0.021926 seconds | ||
+ | </ | ||
+ | |||
+ | On subsequent runs, the same delta was observed between [[fuss: | ||
+ | |||
+ | ===== Strings ===== | ||
+ | |||
+ | For strings, the procedure is similar, just that to be in-tone with UTF-8 and allow the binary-search to work with non-latin characters, we need to use '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Ord() function, written by Pedro Oval, 2010-05-28 | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer Ord(string chr) { | ||
+ | if (chr == "" | ||
+ | string hex = llEscapeURL(chr); | ||
+ | if (llGetSubString(hex, | ||
+ | return llBase64ToInteger(" | ||
+ | llStringToBase64(llGetSubString(chr, | ||
+ | integer b = (integer)(" | ||
+ | if (b < 194 || b > 244) return b; | ||
+ | if (b < 224) return ((b & 0x1F) << 6) | | ||
+ | (integer)(" | ||
+ | if (b < 240) return (b & 0x0F) << 12 + | ||
+ | ((integer)(" | ||
+ | (integer)(" | ||
+ | return (b & 0x07) << 18 + | ||
+ | ((integer)(" | ||
+ | ((integer)(" | ||
+ | (integer)(" | ||
+ | } | ||
+ | |||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasOrdCompare(string A, string B) { | ||
+ | if(A == "" | ||
+ | if(A != "" | ||
+ | if(A == "" | ||
+ | | ||
+ | integer i = Ord(llToUpper(llGetSubString(A, | ||
+ | integer j = Ord(llToUpper(llGetSubString(B, | ||
+ | | ||
+ | if(i < j) return 1; | ||
+ | if(i > j) return -1; | ||
+ | | ||
+ | return wasOrdCompare( | ||
+ | llGetSubString(A, | ||
+ | llGetSubString(B, | ||
+ | ); | ||
+ | } | ||
+ | |||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasBinarySearch(list data, string item) { | ||
+ | integer l = llGetListLength(data); | ||
+ | if(l == 0) return ""; | ||
+ | integer m = l/2; | ||
+ | integer comp = wasOrdCompare(llList2String(data, | ||
+ | if(comp == 0) return item; | ||
+ | if(comp == 1) { | ||
+ | data = llDeleteSubList(data, | ||
+ | jump continue; | ||
+ | } | ||
+ | data = llDeleteSubList(data, | ||
+ | @continue; | ||
+ | return wasBinarySearch(data, | ||
+ | } | ||
+ | </ | ||
+ | ====== Day Of Week ====== | ||
+ | |||
+ | Adapted from [[http:// | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasDayOfWeek(integer d, integer m, integer y) { | ||
+ | if(m<3) { | ||
+ | m+=13; | ||
+ | --y; | ||
+ | jump done; | ||
+ | } | ||
+ | ++m; | ||
+ | @done; | ||
+ | return llList2String([ " | ||
+ | (d + ((26 * m) / 10) + y + (y / 4) - (y / 100) + (y / 400) + 5) % 7); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Modular Exponentiation ====== | ||
+ | |||
+ | ===== With O(n) Complexity ===== | ||
+ | |||
+ | Computes $a^{b} \mod{m}$ with $O(b)$ complexity. | ||
+ | |||
+ | <code lsl2> | ||
+ | integer wasModExp(integer a, integer b, integer m) { | ||
+ | integer res = 1; | ||
+ | integer i = b; | ||
+ | do { | ||
+ | res *= a; | ||
+ | res %= m; | ||
+ | } while(--i> | ||
+ | return res % m; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== With O(log(n)) Complexity ===== | ||
+ | |||
+ | $$ | ||
+ | a^{b} = | ||
+ | \left\{ | ||
+ | \begin{array}{ll} | ||
+ | (a^{2})^{\frac{b}{2}} | ||
+ | a*(a^{2})^{\frac{b-1}{2}} & \mbox{if } b \mbox{ is odd} \\ | ||
+ | 1 & \mbox{if } b = 0 | ||
+ | \end{array} | ||
+ | \right. | ||
+ | $$ | ||
+ | |||
+ | <code lsl2> | ||
+ | integer wasModExp(integer a, integer b, integer m) { | ||
+ | integer x = 1; | ||
+ | integer y = a; | ||
+ | while (b > 0) { | ||
+ | if (b % 2 == 1) x = (x*y) % m; | ||
+ | y = (y*y) % m; | ||
+ | b /= 2; | ||
+ | } | ||
+ | return x % m; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Check if Number is Prime ====== | ||
+ | |||
+ | Uses [[fuss: | ||
+ | |||
+ | Example: | ||
+ | <code lsl2> | ||
+ | wasIsPrime(7, | ||
+ | </ | ||
+ | returns '' | ||
+ | |||
+ | Example: | ||
+ | <code lsl2> | ||
+ | wasIsPrime(24, | ||
+ | </ | ||
+ | returns '' | ||
+ | |||
+ | Uses Fermat' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasIsPrime(integer n, integer k) { | ||
+ | if(k==0) return 1; | ||
+ | integer a = 1+(integer)llFrand(n-1); | ||
+ | if(wasModExp(a, | ||
+ | return wasIsPrime(n, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Date to Unix Timestamp ====== | ||
+ | |||
+ | From the Linux kernel, converts '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasDateTimeToStamp( | ||
+ | integer year, | ||
+ | integer month, | ||
+ | integer day, | ||
+ | integer hour, | ||
+ | integer minute, | ||
+ | integer second | ||
+ | ) { | ||
+ | month -= 2; | ||
+ | if (month <= 0) { | ||
+ | month += 12; | ||
+ | --year; | ||
+ | } | ||
+ | return | ||
+ | ( | ||
+ | (((year / 4 - year / 100 + year / 400 + (367 * month) / 12 + day) + | ||
+ | year * 365 - 719499 | ||
+ | ) * 24 + hour | ||
+ | ) * 60 + minute | ||
+ | ) * 60 + second; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Activity Spinner ====== | ||
+ | |||
+ | The following function will rotate the spinner once and can be used separately in scripts. | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list spinChars = [ " | ||
+ | string wasSpin() { | ||
+ | string text = llList2String(llGetLinkPrimitiveParams(LINK_THIS, | ||
+ | do { | ||
+ | string tok = llGetSubString(text, | ||
+ | if(!~llListFindList(spinChars, | ||
+ | text = llDeleteSubString(text, | ||
+ | } while(llStringLength(text)); | ||
+ | @show; | ||
+ | string next = llList2String(spinChars, | ||
+ | spinChars = llDeleteSubList(spinChars, | ||
+ | spinChars += next; | ||
+ | return " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | To make the spinner move one step forward, call: | ||
+ | <code lsl2> | ||
+ | spin() | ||
+ | </ | ||
+ | which returns [↗] where ↗ is the next position of the arrow. | ||
+ | |||
+ | You can find some collected sets of spinners on the [[assets/ | ||
+ | ====== Decompose a Number into Prime Factors ====== | ||
+ | |||
+ | This is the standard Euler division method which is preferable to the sieve method since the sieve method would require to allocate a list of integers that would surpass the memory constraints of LSL scripts. | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasPrimeFactors(integer n) { | ||
+ | list result = []; | ||
+ | integer div = 2; | ||
+ | do { | ||
+ | if(n%div == 0) { | ||
+ | n = n/div; | ||
+ | result += div; | ||
+ | jump continue; | ||
+ | } | ||
+ | ++div; | ||
+ | @continue; | ||
+ | } while(n != 1); | ||
+ | return result; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Randomly Rotate Times by Angle ====== | ||
+ | |||
+ | When this function is called, it will rotate the primitive it is in '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | wasRandomRotate(integer times, float angle) { | ||
+ | if(times <= 0) return; | ||
+ | integer x = (integer)llFrand(2); | ||
+ | integer y = (integer)llFrand(2); | ||
+ | integer z = (integer)llFrand(2); | ||
+ | llSetRot(llGetRot()*llEuler2Rot(< | ||
+ | wasRandomRotate(--times, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | If rotation is desired on one single axis only, the following modification will do that: | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | wasRandomRotate(integer times, float angle) { | ||
+ | if(times <= 0) return; | ||
+ | integer x = (integer)llFrand(2); | ||
+ | if(x == 1) jump rotate; | ||
+ | integer y = (integer)llFrand(2); | ||
+ | if(y == 1) jump rotate; | ||
+ | integer z = (integer)llFrand(2); | ||
+ | @rotate; | ||
+ | llSetRot(llGetRot()*llEuler2Rot(< | ||
+ | wasRandomRotate(--times, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Count Consecutive Characters in Strings ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasConsecutiveCharacterMatches(string hay, string needle) { | ||
+ | if(llStringLength(hay) == 0 || llStringLength(needle) == 0) return 0; | ||
+ | integer count = 0; | ||
+ | string a = llGetSubString(hay, | ||
+ | string b = llGetSubString(needle, | ||
+ | if(a == b) { | ||
+ | ++count; | ||
+ | hay = llDeleteSubString(hay, | ||
+ | needle = llDeleteSubString(needle, | ||
+ | jump continue; | ||
+ | } | ||
+ | if(llStringLength(hay) > llStringLength(needle)) { | ||
+ | hay = llDeleteSubString(hay, | ||
+ | jump continue; | ||
+ | } | ||
+ | if(llStringLength(hay) < llStringLength(needle)) { | ||
+ | needle = llDeleteSubString(needle, | ||
+ | jump continue; | ||
+ | } | ||
+ | hay = llDeleteSubString(hay, | ||
+ | needle = llDeleteSubString(needle, | ||
+ | @continue; | ||
+ | return count + wasConsecutiveCharacterMatches(hay, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== String to Vector or Rotation ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | vector v = (vector)"< | ||
+ | rotation r = (rotation)"< | ||
+ | </ | ||
+ | |||
+ | |||
+ | ====== List to Vector or Rotation ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | list a = [ " | ||
+ | vector v = (vector)("<" | ||
+ | |||
+ | a = [ " | ||
+ | rotation r = (rotation)("<" | ||
+ | </ | ||
+ | |||
+ | ====== Mixed-Type List to Type ====== | ||
+ | |||
+ | The following function converts a list of mixed types to a list of type '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasCompoundToList(integer T, list compound) { | ||
+ | if(llGetListLength(compound) == 0) return []; | ||
+ | if(T == TYPE_FLOAT) return llList2Float(compound, | ||
+ | wasCompoundToList(T, | ||
+ | if(T == TYPE_INTEGER) return llList2Integer(compound, | ||
+ | wasCompoundToList(T, | ||
+ | if(T == TYPE_STRING) return llList2String(compound, | ||
+ | wasCompoundToList(T, | ||
+ | if(T == TYPE_KEY) return llList2Key(compound, | ||
+ | wasCompoundToList(T, | ||
+ | if(T == TYPE_VECTOR) return llList2Vector(compound, | ||
+ | wasCompoundToList(T, | ||
+ | if(T == TYPE_ROTATION) return llList2Rot(compound, | ||
+ | wasCompoundToList(T, | ||
+ | return wasCompoundToList(T, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | For example, suppose you have a compound list: | ||
+ | |||
+ | <code lsl2> | ||
+ | list a = [ 0, NULL_KEY, 3.546 ]; | ||
+ | </ | ||
+ | |||
+ | and you want to convert all the values to a float type. You would then do: | ||
+ | |||
+ | <code lsl2> | ||
+ | llOwnerSay( | ||
+ | llDumpList2String( | ||
+ | wasCompoundToList(TYPE_FLOAT, | ||
+ | "," | ||
+ | ); | ||
+ | </ | ||
+ | |||
+ | which would say to the owner: | ||
+ | |||
+ | <code lsl2> | ||
+ | 0.000000, | ||
+ | </ | ||
+ | |||
+ | An example of the usefulness of the function can be found on the [[secondlife: | ||
+ | |||
+ | ====== Vector or Rotation To List of Type ====== | ||
+ | |||
+ | '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasCompoundToType(integer T, list compound) { | ||
+ | integer S = llGetListEntryType(compound, | ||
+ | if(S != TYPE_VECTOR && S != TYPE_ROTATION) return []; | ||
+ | list a = llParseString2List((string)compound, | ||
+ | compound = []; | ||
+ | do { | ||
+ | if(T == TYPE_FLOAT) | ||
+ | compound += llList2Float(a, | ||
+ | if(T == TYPE_INTEGER) | ||
+ | compound += llList2Integer(a, | ||
+ | if(T == TYPE_STRING) | ||
+ | compound += llList2String( | ||
+ | llParseString2List((string)a, | ||
+ | , 0); | ||
+ | a = llDeleteSubList(a, | ||
+ | } while(llGetListLength(a) != 0); | ||
+ | return compound; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This is useful in situations where you would like to retrieve the components of a '' | ||
+ | |||
+ | <code lsl2> | ||
+ | vector p = <23.499, 134, 39.048245>; | ||
+ | list intList = wasCompoundToType(TYPE_INTEGER, | ||
+ | </ | ||
+ | |||
+ | the list '' | ||
+ | |||
+ | Similarly, after the sequence: | ||
+ | <code lsl2> | ||
+ | vector p = <23.499, 134, 39.048245>; | ||
+ | list floatList = wasCompoundToType(TYPE_FLOAT, | ||
+ | </ | ||
+ | |||
+ | the list '' | ||
+ | |||
+ | When a string type is requested: | ||
+ | <code lsl2> | ||
+ | vector p = <23.499, 134, 39.048245>; | ||
+ | list truncatedStringList = wasCompoundToType(TYPE_STRING, | ||
+ | </ | ||
+ | |||
+ | the list '' | ||
+ | |||
+ | If any type other than '' | ||
+ | |||
+ | The following is an example from [[secondlife: | ||
+ | <code lsl2> | ||
+ | string slurl = llGetRegionName() + "/" | ||
+ | llDumpList2String( | ||
+ | llList2ListStrided(llParseString2List((string)llGetPos(), | ||
+ | "/" | ||
+ | </ | ||
+ | |||
+ | and so is this: | ||
+ | <code lsl2> | ||
+ | vector p = llGetPos(); | ||
+ | string slurl = llGetRegionName() + "/" | ||
+ | (string)((integer)p.x) + "/" | ||
+ | (string)((integer)p.y) + "/" | ||
+ | (string)((integer)p.z); | ||
+ | </ | ||
+ | |||
+ | However, instead of writing a really large line and to avoid allocating a new variable '' | ||
+ | <code lsl2> | ||
+ | string slurl = llGetRegionName() + "/" | ||
+ | llDumpList2String( | ||
+ | wasCompoundToType(TYPE_INTEGER, | ||
+ | "/" | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ====== Merge Sort ====== | ||
+ | |||
+ | Example: | ||
+ | <code lsl2> | ||
+ | wasMergeSort([20, | ||
+ | </ | ||
+ | |||
+ | Returns the list: | ||
+ | < | ||
+ | 1 3 4 5 7 8 11 12 20 32 | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasMergeSortMerge(list left, list right) { | ||
+ | if(!llGetListLength(left) || !llGetListLength(right)) return left+right; | ||
+ | integer l = llList2Integer(left, | ||
+ | integer r = llList2Integer(right, | ||
+ | if(l <= r) return l + wasMergeSortMerge(llDeleteSubList(left, | ||
+ | return r + wasMergeSortMerge(left, | ||
+ | } | ||
+ | |||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasMergeSort(list input) { | ||
+ | integer d = llGetListLength(input); | ||
+ | if(d <= 1) return input; | ||
+ | integer m = d/2; | ||
+ | return wasMergeSortMerge( | ||
+ | wasMergeSort( | ||
+ | llList2List(input, | ||
+ | ), | ||
+ | wasMergeSort( | ||
+ | llList2List(input, | ||
+ | ) | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Private Channel Key-Hash ====== | ||
+ | |||
+ | Extremely useful, taken from the '' | ||
+ | |||
+ | **Inline Usage** | ||
+ | <code lsl2> | ||
+ | integer comChannel = ((integer)(" | ||
+ | </ | ||
+ | |||
+ | it hashes keys to integers and makes a great method of generating private, negative channels. | ||
+ | |||
+ | ===== Simplified Alternative ===== | ||
+ | |||
+ | Suppose we have an arbitrary UUID such as: | ||
+ | |||
+ | < | ||
+ | 84c08dd4-7009-6b21-4645-d0d3ed5265df | ||
+ | </ | ||
+ | |||
+ | We know that integer types start from '' | ||
+ | < | ||
+ | 84c08dd | ||
+ | </ | ||
+ | |||
+ | Now, we know that we want a negative number, so we concatenate '' | ||
+ | < | ||
+ | 0x884c08dd | ||
+ | </ | ||
+ | |||
+ | and casting that string to an integer, this will give us '' | ||
+ | |||
+ | Now we test the base-cases: | ||
+ | |||
+ | * Suppose that the key supplied is the '' | ||
+ | * Suppose that the key supplied is a maximal key, such that all positions are set to '' | ||
+ | |||
+ | Thus, regardless what the first '' | ||
+ | |||
+ | The code is a one liner: | ||
+ | |||
+ | <code lsl2> | ||
+ | integer comChannel = (integer)(" | ||
+ | </ | ||
+ | |||
+ | and reduces the operations performed by the previous version. | ||
+ | |||
+ | Since there are '' | ||
+ | |||
+ | $$ | ||
+ | \frac{1}{{{16}\choose{7}}} = \frac{1}{\frac{16!}{(16-7)!7!}} = \frac{1}{11440} \approx 0.000087412587413 | ||
+ | $$ | ||
+ | |||
+ | which would probabilistically imply that after $11440$ UUIDs one could expect a collision. | ||
+ | |||
+ | Now this is very decent, in fact it is pretty redundant that we could have chosen a smaller nibble to work with. Suppose that you have an object listening on a region to an avatar using this hash. Probabilistically, | ||
+ | |||
+ | ====== Count Elements in List Excluding Certain Elements ====== | ||
+ | |||
+ | The following function returns the number of elements in a list excluding a certain string. For example, given a list such as: | ||
+ | |||
+ | <code lsl2> | ||
+ | list input = [" | ||
+ | </ | ||
+ | when called with: | ||
+ | <code lsl2> | ||
+ | wasListCount(input, | ||
+ | </ | ||
+ | the function will return '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasListCountExclude(list input, list exclude) { | ||
+ | if(llGetListLength(input) == 0) return 0; | ||
+ | if(llListFindList(exclude, | ||
+ | return 1 + wasListCountExclude(llDeleteSubList(input, | ||
+ | return wasListCountExclude(llDeleteSubList(input, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Merge Lists ====== | ||
+ | |||
+ | The following function will merge two lists '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasListMerge(list l, list m, string merge) { | ||
+ | if(llGetListLength(l) == 0 && llGetListLength(m) == 0) return []; | ||
+ | string a = llList2String(m, | ||
+ | if(a != merge) return [ a ] + wasListMerge(l, | ||
+ | return [ llList2String(l, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | To understand this better, suppose you have a table using the lists '' | ||
+ | |||
+ | ^ Index ^ 0 ^ 1 ^ 2 ^ | ||
+ | | List a | 1 | "" | ||
+ | | List b | "" | ||
+ | |||
+ | The algorithm goes through list '' | ||
+ | * If the value of the element of list '' | ||
+ | * Otherwise, it will prefer the value of the element of '' | ||
+ | * If the value of '' | ||
+ | |||
+ | ===== Example ===== | ||
+ | |||
+ | Suppose we have the following lists: | ||
+ | <code lsl2> | ||
+ | list a = [ 1, "", | ||
+ | list b = [ "", | ||
+ | </ | ||
+ | when calling the function with: | ||
+ | <code lsl2> | ||
+ | wasListMerge(a, | ||
+ | </ | ||
+ | it will return the list: | ||
+ | <code lsl2> | ||
+ | [ 1, b, 2 ]; | ||
+ | </ | ||
+ | |||
+ | ===== Counter-Example ===== | ||
+ | |||
+ | Suppose we have the following lists: | ||
+ | <code lsl2> | ||
+ | list a = [ 1, "", | ||
+ | list b = [ " | ||
+ | </ | ||
+ | when calling the function with: | ||
+ | <code lsl2> | ||
+ | wasListMerge(a, | ||
+ | </ | ||
+ | it will return the list: | ||
+ | <code lsl2> | ||
+ | [ " | ||
+ | </ | ||
+ | |||
+ | ===== Practical Example ======== | ||
+ | |||
+ | Suppose that we have a menu list that is passed to '' | ||
+ | |||
+ | <code lsl2> | ||
+ | list menu = [ " | ||
+ | </ | ||
+ | |||
+ | and we want to insert ''< | ||
+ | |||
+ | | Black | ||
+ | | Red | Green | White | | ||
+ | | ''< | ||
+ | |||
+ | with the ''< | ||
+ | |||
+ | <code lsl2> | ||
+ | wasListMerge(menu, | ||
+ | </ | ||
+ | |||
+ | The empty string between the buttons indicates that an element from the '' | ||
+ | |||
+ | ====== Endless Menu with Custom Buttons ====== | ||
+ | |||
+ | Using the two functions defined previously [[fuss: | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasMenuIndex = 0; | ||
+ | list wasDialogMenu(list input, list actions, string direction) { | ||
+ | integer cut = 11-wasListCountExclude(actions, | ||
+ | if(direction == ">" | ||
+ | ++wasMenuIndex; | ||
+ | jump slice; | ||
+ | } | ||
+ | if(direction == "<" | ||
+ | --wasMenuIndex; | ||
+ | jump slice; | ||
+ | } | ||
+ | @slice; | ||
+ | integer multiple = wasMenuIndex*cut; | ||
+ | input = llList2List(input, | ||
+ | input = wasListMerge(input, | ||
+ | return input; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This refines the previously outdated method (which has now been removed) by allowing custom buttons to be inserted and processed. | ||
+ | |||
+ | ====== Point Generation ====== | ||
+ | |||
+ | The following sub-routines generate points within various bodies and shapes. | ||
+ | |||
+ | {{indexmenu> | ||
+ | ====== Key-Value Data ====== | ||
+ | |||
+ | The following functions allow processing of key-value data. An overview is provided on the [[.: | ||
+ | |||
+ | {{indexmenu> | ||
+ | ====== Empty String or Blank ====== | ||
+ | |||
+ | Similar to '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasIsSpace(string input) { | ||
+ | if(input == "" | ||
+ | list split = llParseString2List(llEscapeURL(input), | ||
+ | do { | ||
+ | string code = llList2String(split, | ||
+ | if(code != " | ||
+ | split = llDeleteSubList(split, | ||
+ | } while(llGetListLength(split)); | ||
+ | return TRUE; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Sentence Sub-Sequence Match ====== | ||
+ | |||
+ | The function expects '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasMatchSubSentence(string input, string match) { | ||
+ | list data = llParseString2List(input, | ||
+ | do { | ||
+ | if(llList2String(data, | ||
+ | data = llDeleteSubList(data, | ||
+ | } while(llGetListLength(data)); | ||
+ | return FALSE; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Relative Positions ====== | ||
+ | |||
+ | {{fuss_lsl_relative_positions.png}} | ||
+ | |||
+ | Suppose we have two points in space, $A$ and $B$. First, we pick either object as a point of reference, say $A$. Then we calculate: | ||
+ | |||
+ | $$ | ||
+ | T=\frac{-(A_{position}-B_{position})}{A_{rotation}} | ||
+ | $$ | ||
+ | |||
+ | Which gives us a translation from point $A$ to point $B$. Finally, to get the new position of object $B$, we perform: | ||
+ | |||
+ | $$ | ||
+ | B_{position} = A_{position}+T*A_{rotation} | ||
+ | $$ | ||
+ | |||
+ | In LSL terms, if we have an object '' | ||
+ | <code lsl2> | ||
+ | vector A = <xa, ya, za> | ||
+ | </ | ||
+ | We also know the rotation of object '' | ||
+ | <code lsl2> | ||
+ | rotation RA = <xar, yar, zar, sar>; | ||
+ | </ | ||
+ | |||
+ | Next, let's say we the object '' | ||
+ | <code lsl2> | ||
+ | vector B = <xb, yb, zb> | ||
+ | </ | ||
+ | |||
+ | Now, if we rotate and move object '' | ||
+ | |||
+ | In order to do that, we calculate the translation vector '' | ||
+ | <code lsl2> | ||
+ | vector T = -(A-B)/RA; | ||
+ | </ | ||
+ | which we can store. | ||
+ | |||
+ | Later, we can set the position of object '' | ||
+ | <code lsl2> | ||
+ | vector NEW_B = A + T*RA. | ||
+ | </ | ||
+ | |||
+ | This is used in the [[secondlife: | ||
+ | ====== Relative Rotations ====== | ||
+ | |||
+ | {{fuss_lsl_relative_rotations.png}} | ||
+ | |||
+ | |||
+ | Suppose we have a reference rotation in quaternions $Q_{reference}$ and a final rotation $Q_{final}$. We want to find out what relative rotation $Q_{transition}$ we have to apply to $Q_{reference}$ in order to get $Q_{final}$. | ||
+ | |||
+ | The problem can be solved easily since we know that the final rotation will be the original reference rotation composed with " | ||
+ | |||
+ | $$ | ||
+ | Q_{reference} * Q_{transition} = Q_{final} | ||
+ | $$ | ||
+ | |||
+ | This $Q_{transition}$ represents a difference between the original reference rotation and the final rotation that we want to achieve. | ||
+ | |||
+ | This difference, a transition rotation, is given by: | ||
+ | $$ | ||
+ | Q_{transition}=Q_{reference}^{-1}*Q_{final} | ||
+ | $$ | ||
+ | |||
+ | To obtain $Q_{reference}^{-1}$ we either negate the real part component or we can just negate the $x,y,z$ components. | ||
+ | |||
+ | Suppose that: | ||
+ | |||
+ | $$ | ||
+ | Q_{reference} = < | ||
+ | $$ | ||
+ | |||
+ | then: | ||
+ | $$ | ||
+ | Q_{reference}^{-1} = < | ||
+ | $$ | ||
+ | |||
+ | or: | ||
+ | |||
+ | $$ | ||
+ | Q_{refernece}^{-1} = < | ||
+ | $$ | ||
+ | |||
+ | Finally, we rotate the object with: | ||
+ | |||
+ | $$ | ||
+ | Q_{final} = Q_{reference}*Q_{transition} | ||
+ | $$ | ||
+ | |||
+ | In LSL terms, suppose that we have a reference rotation: | ||
+ | <code lsl2> | ||
+ | rotation RA = <a.x, a.y, a.z, a.s> | ||
+ | </ | ||
+ | and suppose we have the final rotation: | ||
+ | <code lsl2> | ||
+ | rotation RB = <b.x, b.y, b.z, b.s> | ||
+ | </ | ||
+ | we first calculate the transition rotation: | ||
+ | <code lsl2> | ||
+ | rotation T = < | ||
+ | </ | ||
+ | and we set the new object' | ||
+ | <code lsl2> | ||
+ | llSetRot(RA * T); | ||
+ | </ | ||
+ | |||
+ | This was used for the [[secondlife: | ||
+ | |||
+ | ====== Ord ====== | ||
+ | |||
+ | Returns an integer with the Unicode code of the first character in '' | ||
+ | |||
+ | <code lsl2> | ||
+ | // Ord() function, written by Pedro Oval, 2010-05-28 | ||
+ | // Inlined by Wizardry and Steamworks | ||
+ | integer Ord(string chr) { | ||
+ | if (chr == "" | ||
+ | string hex = llEscapeURL(chr); | ||
+ | if (llGetSubString(hex, | ||
+ | return llBase64ToInteger(" | ||
+ | llStringToBase64(llGetSubString(chr, | ||
+ | integer b = (integer)(" | ||
+ | if (b < 194 || b > 244) return b; | ||
+ | if (b < 224) return ((b & 0x1F) << 6) | | ||
+ | (integer)(" | ||
+ | if (b < 240) return (b & 0x0F) << 12 + | ||
+ | ((integer)(" | ||
+ | (integer)(" | ||
+ | return (b & 0x07) << 18 + | ||
+ | ((integer)(" | ||
+ | ((integer)(" | ||
+ | (integer)(" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Binary Trees and Binary Search Trees ====== | ||
+ | |||
+ | {{indexmenu> | ||
+ | |||
+ | ====== llListFindList and Keys ====== | ||
+ | |||
+ | Suppose you store keys as strings in a list called '' | ||
+ | |||
+ | <code lsl2> | ||
+ | if (llListFindList(aList, | ||
+ | </ | ||
+ | |||
+ | |||
+ | ====== Event Handler Order ====== | ||
+ | |||
+ | ^ Event ^ Handler State Machine | ||
+ | | Attach Object (from inventory) | ->'' | ||
+ | | Attach Object (from ground) | ||
+ | | Detach Object (from inventory) | ->'' | ||
+ | | Log-in with item attached | ||
+ | | Logout with item attached | ||
+ | | State-change | ||
+ | | Rez in-world | ||
+ | |||
+ | ====== LID™ ====== | ||
+ | |||
+ | As explained by the [[secondlife: | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | LID(key id) { | ||
+ | if(id != NULL_KEY && llGetAttached() != 0) { | ||
+ | llSetLinkPrimitiveParamsFast(2, | ||
+ | llResetScript(); | ||
+ | } | ||
+ | llSetLinkPrimitiveParamsFast(2, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | has to be called in the '' | ||
+ | |||
+ | <code lsl2> | ||
+ | attach(key id) { | ||
+ | LID(id); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ====== RGB to Color ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | vector wasRGBToColor(vector rgb) { | ||
+ | return <rgb.x / 255, rgb.y / 255, rgb.z / 255>; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | ====== Color to RGB ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | vector wasColorToRGB(vector color) { | ||
+ | return <color.x * 255, color.y * 255, color.z * 255>; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | ====== Center String for Dialog Message ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasDialogCenterASCII(string in) { | ||
+ | integer l = llStringLength(in); | ||
+ | if(l > 47) return in; | ||
+ | integer s = (47-l)/2; | ||
+ | string pad = ""; | ||
+ | do { | ||
+ | pad += " "; | ||
+ | } while(--s> | ||
+ | return pad + in + pad; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== RVL Trap Relay ====== | ||
+ | |||
+ | '' | ||
+ | |||
+ | Very few changes were performed, mostly jump-table optimizations and using the '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | // Original by: By Marine Kelley, Maike Short and Felis Darwin | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | wasTrapCommand(key id, string message) { | ||
+ | list tokens = llParseString2List (message, ["," | ||
+ | if (llGetListLength (tokens) != 3) return; | ||
+ | string cmd_id = llList2String(tokens, | ||
+ | if (llList2Key(tokens, | ||
+ | list commands = llParseString2List(llList2String(tokens, | ||
+ | do { | ||
+ | string command = llList2String(commands, | ||
+ | | ||
+ | // relay metacommands | ||
+ | if(command == " | ||
+ | do { | ||
+ | string restriction = llList2String(_relayRestrictions, | ||
+ | if(llGetSubString(restriction, | ||
+ | llOwnerSay(restriction + " | ||
+ | @next; | ||
+ | _relayRestrictions = llDeleteSubList(_relayRestrictions, | ||
+ | } while(llGetListLength(_relayRestrictions) != 0); | ||
+ | llShout(-1812221819, | ||
+ | return; | ||
+ | } | ||
+ | | ||
+ | if (command == " | ||
+ | llShout(-1812221819, | ||
+ | jump continue; | ||
+ | } | ||
+ | | ||
+ | // standard commands | ||
+ | if (llGetSubString(command, | ||
+ | | ||
+ | list commandTokens = llParseString2List (command, [" | ||
+ | string behav = llList2String(commandTokens, | ||
+ | string param = llList2String(commandTokens, | ||
+ | integer idx = llListFindList(_relayRestrictions, | ||
+ | | ||
+ | if ((param == " | ||
+ | llOwnerSay(behav + " | ||
+ | _relayRestrictions += [behav]; | ||
+ | jump acknowledge; | ||
+ | } | ||
+ | if ((param == " | ||
+ | llOwnerSay(llList2String(_relayRestrictions, | ||
+ | _relayRestrictions = llDeleteSubList(_relayRestrictions, | ||
+ | jump acknowledge; | ||
+ | } | ||
+ | llOwnerSay(command); | ||
+ | @acknowledge; | ||
+ | llShout(-1812221819, | ||
+ | @continue; | ||
+ | commands = llDeleteSubList(commands, | ||
+ | } while(llGetListLength(commands) != 0); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Elipse Circumference ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | float wasElipseCircumference(float a, float b, integer precision) { | ||
+ | float x = llListStatistics(LIST_STAT_MAX, | ||
+ | float y = llListStatistics(LIST_STAT_MIN, | ||
+ | float tol = llSqrt(llPow(.5, | ||
+ | if (precision * y < tol * x) return 4 * x; | ||
+ | float s = 0; | ||
+ | float m = 1; | ||
+ | while (x - y > tol * y) { | ||
+ | x = .5 * (x + y); | ||
+ | y = llSqrt(x * y); | ||
+ | m *= 2; | ||
+ | s += m * llPow(x - y, 2); | ||
+ | } | ||
+ | return PI * (llPow(a + b, 2) - s) / (x + y); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Point in Polygon ====== | ||
+ | |||
+ | Given a point, represented by a vector '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | // www.ecse.rpi.edu/ | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasPointInPolygon(vector p, list polygon) { | ||
+ | integer inside = FALSE; | ||
+ | integer i = 0; | ||
+ | integer nvert = llGetListLength(polygon); | ||
+ | integer j = nvert-1; | ||
+ | do { | ||
+ | vector pi = llList2Vector(polygon, | ||
+ | vector pj = llList2Vector(polygon, | ||
+ | if (((pi.y > p.y) != (pj.y > p.y)) && | ||
+ | (p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x)) { | ||
+ | inside = !inside; | ||
+ | } | ||
+ | j = i++; | ||
+ | } while(i< | ||
+ | return inside; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The function returns '' | ||
+ | |||
+ | ====== Path Simplification Algorithm ====== | ||
+ | |||
+ | {{fuss_path_simplification_algorithm.gif}} | ||
+ | |||
+ | Given a set of points in space, the following algorithm simplifies the path so that no distance between two points from the set is smaller than '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasPathSimplify(list path, float segment) { | ||
+ | if(llGetListLength(path) == 0) return path; | ||
+ | | ||
+ | // grab first two points... | ||
+ | vector P = wasStringToVector(llList2String(path, | ||
+ | path = llDeleteSubList(path, | ||
+ | if(llGetListLength(path) == 0) return path + [P]; | ||
+ | vector Q = wasStringToVector(llList2String(path, | ||
+ | path = llDeleteSubList(path, | ||
+ | |||
+ | // if they exceed or equal the segment length | ||
+ | if(llVecDist(P, | ||
+ | // save the previous one (P) and, | ||
+ | // push the next one onto the stack (Q). | ||
+ | // wend | ||
+ | return [ P ] + wasPathSimplify([ Q ] + path, segment); | ||
+ | } | ||
+ | // otherwise, make the mean between them... | ||
+ | float dist = llVecDist(P, | ||
+ | // ...and push it back onto the stack | ||
+ | // wend | ||
+ | return wasPathSimplify([ P + ((dist/ | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Partition a String ====== | ||
+ | |||
+ | Optimistically partitions a string '' | ||
+ | |||
+ | Given a string with value '' | ||
+ | < | ||
+ | llOwnerSay(" | ||
+ | </ | ||
+ | |||
+ | will return a list: | ||
+ | <code lsl2> | ||
+ | [ " | ||
+ | </ | ||
+ | |||
+ | The function operates by divide and conquer and takes care to not return the empty string in the borderline case $length(data) \leq n$. | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasPartitionString(string data, integer n) { | ||
+ | string left = llGetSubString(data, | ||
+ | string right = llGetSubString(llDeleteSubString(data, | ||
+ | | ||
+ | if(llStringLength(left) <= n && llStringLength(right) <= n) { | ||
+ | if(llStringLength(left) == 0) return [ right ]; | ||
+ | if(llStringLength(right) == 0) return [ left ]; | ||
+ | return [left, right]; | ||
+ | } | ||
+ | |||
+ | if(llStringLength(right) <= n) | ||
+ | return wasPartitionString(left, | ||
+ | | ||
+ | if(llStringLength(left) <= n) | ||
+ | return [ left ] + wasPartitionString(right, | ||
+ | | ||
+ | return wasPartitionString(data, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Repeat a String ====== | ||
+ | |||
+ | A call such as: | ||
+ | <code lsl2> | ||
+ | wasRepeatString(" | ||
+ | </ | ||
+ | |||
+ | will return the string: | ||
+ | <code lsl2> | ||
+ | ✪✪ ✪✪ ✪✪ ✪✪ | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasRepeatString(string in, string sep, integer num) { | ||
+ | if(num == 0) return ""; | ||
+ | list i = llParseString2List(in, | ||
+ | integer l = llGetListLength(i); | ||
+ | if(l >= num) return llDumpList2String(llList2List(i, | ||
+ | return wasRepeatString(in + sep + in, sep, num); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Rotate a Linked Primitive around Other Linked Primitives ====== | ||
+ | |||
+ | {{fuss_lsl_rotate_around_linked_primitive.gif}} | ||
+ | |||
+ | Sometimes it is useful to rotate a primitive around another linked primitive, for example, for creating a planetarium or gear mechanics where '' | ||
+ | |||
+ | The function is to be used inside the primitive to roate and takes as parameter a link number, an angle to rotate by and a plane vector. These are explained as: | ||
+ | |||
+ | * the link number is the primitive to rotate around (in the animation above, the centre sphere representing the sun). | ||
+ | * the angle is the angle in degrees to rotate by ($0 \ldots 360$) in the trigonometric sense. | ||
+ | * the plane vector gives the plane in which to rotate ($x,y$, $x,z$, $y,z$) given here as normal vectors, as in, the plane defined by the axes $x$ and $y$ will be the plane vector ''< | ||
+ | |||
+ | The illustrated animation is created using the following loop: | ||
+ | <code lsl2> | ||
+ | default { | ||
+ | state_entry() { | ||
+ | @repeat; | ||
+ | integer a = 0; | ||
+ | do { | ||
+ | llSetLinkPrimitiveParamsFast(LINK_THIS, | ||
+ | } while(++a != 360); | ||
+ | jump repeat; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | where the function '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Returns a vector with the position of this linked primitive rotated | ||
+ | // angle degrees around a linked primitive with link number link_num, in // | ||
+ | // the plane given by the vector plane. | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | vector wasRotateAroundLinkNumber(integer link_num, float angle, vector plane) { | ||
+ | vector rp = llList2Vector( | ||
+ | llGetLinkPrimitiveParams(link_num, | ||
+ | , 0); | ||
+ | vector mp = llGetPos(); | ||
+ | vector fp = ZERO_VECTOR; | ||
+ | float r = 0; | ||
+ | if((integer)plane.y && (integer)plane.z) { | ||
+ | r = llVecDist( | ||
+ | <0, mp.y, mp.z>, | ||
+ | <0, rp.y, rp.z> | ||
+ | ); | ||
+ | fp = <mp.x, rp.y + r * llCos(angle*DEG_TO_RAD), | ||
+ | jump set; | ||
+ | } | ||
+ | if((integer)plane.x && (integer)plane.y) { | ||
+ | r = llVecDist( | ||
+ | <mp.x, mp.y, 0>, | ||
+ | <rp.x, rp.y, 0> | ||
+ | ); | ||
+ | fp = <rp.x + r * llCos(angle*DEG_TO_RAD), | ||
+ | jump set; | ||
+ | } | ||
+ | if((integer)plane.x && (integer)plane.z) { | ||
+ | r = llVecDist( | ||
+ | <mp.x, 0, mp.z>, | ||
+ | <rp.x, 0, rp.z> | ||
+ | ); | ||
+ | fp = <rp.x + r * llCos(angle*DEG_TO_RAD), | ||
+ | jump set; | ||
+ | } | ||
+ | @set; | ||
+ | return fp - llGetRootPosition() / llGetRootRotation(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The '' | ||
+ | |||
+ | Here is a linked build where the function may be useful: | ||
+ | |||
+ | {{fuss_lsl_rotate_around_linked_primitive.png? | ||
+ | |||
+ | The yellow cylinder represents the root primitive, and pink ball is supposed to rotate around the blue rod at a given distance from the root primitive. This is accomplished with: | ||
+ | <code lsl2> | ||
+ | default { | ||
+ | state_entry() { | ||
+ | @repeat; | ||
+ | integer a = 0; | ||
+ | do { | ||
+ | llSetLinkPrimitiveParamsFast(LINK_THIS, | ||
+ | llSleep(0.1); | ||
+ | } while(++a != 360); | ||
+ | jump repeat; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Avatar Speed ====== | ||
+ | |||
+ | The following is a list of avatar top-speeds: | ||
+ | |||
+ | ^ Type ^ Top Speed ^ | ||
+ | | Walking | $\approx 3.20 \frac{m}{s}$ | | ||
+ | | Running | $\approx 5.13 \frac{m}{s}$ | | ||
+ | | Flying | ||
+ | |||
+ | ====== Explode and Implode a List ====== | ||
+ | |||
+ | Exploding a list for this function means, on every iteration, to take the centre of the list and move it to the front of the list: | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasExplodeList(list l) { | ||
+ | integer i = llGetListLength(l); | ||
+ | if(i == 0) return []; | ||
+ | return llList2List(l, | ||
+ | wasExplodeList(llDeleteSubList(l, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | An example input is: | ||
+ | <code lsl2> | ||
+ | list a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; | ||
+ | </ | ||
+ | |||
+ | and its corresponding output: | ||
+ | < | ||
+ | 5, | ||
+ | </ | ||
+ | |||
+ | Imploding a list means to take the extremes of the list and to group them at the front of the list: | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasImplodeList(list l) { | ||
+ | integer i = llGetListLength(l); | ||
+ | if(i == 0) return []; | ||
+ | return wasImplodeList(llDeleteSubList(l, | ||
+ | llList2List(l, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | An example input is: | ||
+ | <code lsl2> | ||
+ | list a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; | ||
+ | </ | ||
+ | |||
+ | and its corresponding output: | ||
+ | < | ||
+ | 1, | ||
+ | </ | ||
+ | |||
+ | It is said that Leonhard Euler was asked during class to add-up all the numbers between and including $1$ and $100$ as a punishment. Instead of adding them up sequentially, | ||
+ | |||
+ | Depending on the number of elements in the input list, '' | ||
+ | |||
+ | * In case of an odd number of elements in the input list, '' | ||
+ | |||
+ | * In case of even number of elements in the input list, '' | ||
+ | |||
+ | ====== Escape and Send POST Data ====== | ||
+ | |||
+ | This function takes as input some form data such as: | ||
+ | |||
+ | < | ||
+ | name=CP-3& | ||
+ | </ | ||
+ | |||
+ | and returns a string with the escaped values: | ||
+ | < | ||
+ | name=CP%2D3& | ||
+ | </ | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasKeyValueURIEscape(string data) { | ||
+ | list i = llParseString2List(data, | ||
+ | list output = []; | ||
+ | do { | ||
+ | output += llList2String(i, | ||
+ | i = llDeleteSubList(i, | ||
+ | } while(llGetListLength(i) != 0); | ||
+ | return llDumpList2String(output, | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | The function can be used to send form data to a server-side script, for example: | ||
+ | <code lsl2> | ||
+ | string data = wasKeyValueURIEscape(" | ||
+ | llHTTPRequest(" | ||
+ | </ | ||
+ | will send '' | ||
+ | |||
+ | On the server-side, | ||
+ | <code php> | ||
+ | $name = urldecode($_POST[' | ||
+ | $result = urldecode($_POST[' | ||
+ | $test = urldecode($_POST[' | ||
+ | </ | ||
+ | |||
+ | ====== Determining Whether Two Segments Intersect ====== | ||
+ | |||
+ | Given four points ('' | ||
+ | <code lsl2> | ||
+ | vector A = < | ||
+ | vector B = < | ||
+ | vector C = < | ||
+ | vector D = < | ||
+ | </ | ||
+ | |||
+ | In order to determine whether the segments $AB$ and $CD$ intersect, one would call the function as: | ||
+ | <code lsl2> | ||
+ | if(wasSegmentIntersect(A, | ||
+ | llOwnerSay(" | ||
+ | return; | ||
+ | } | ||
+ | llOwnerSay(" | ||
+ | </ | ||
+ | |||
+ | '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // determines whether the segment AB intersects the segment CD | ||
+ | integer wasSegmentIntersect(vector A, vector B, vector C, vector D) { | ||
+ | vector s1 = <B.x - A.x, B.y - A.y, B.z - A.z>; | ||
+ | vector s2 = <D.x - C.x, D.y - C.y, D.y - C.z>; | ||
+ | |||
+ | float d = (s1.x * s2.y -s2.x * s1.y); | ||
+ | |||
+ | if(d == 0) return FALSE; | ||
+ | |||
+ | float s = (s1.x * (A.y - C.y) - s1.y * (A.x - C.x)) / d; | ||
+ | float t = (s2.x * (A.y - C.y) - s2.y * (A.x - C.x)) / d; | ||
+ | |||
+ | // intersection at <A.x + (t * s1.x), A.y + (t * s1.y), A.z + (t * s1.z)>; | ||
+ | return (integer)(s >= 0 && s <= 1 && t >= 0 && t <= 1 && | ||
+ | A.z + t*(B.z - A.z) == C.z + s*(D.z - C.z)); | ||
+ | } | ||
+ | </ | ||
+ | ====== Determine if Two Avatars or Objects Intersect (Collide) ====== | ||
+ | |||
+ | A collision between two avatars or objects in Second Life means that the two bounding boxes belonging to each avatar or object intersect. | ||
+ | |||
+ | The function '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // returns true if two objects intersect | ||
+ | integer wasObjectIntersect(key O1, key O2) { | ||
+ | list o = llGetBoundingBox(O1); | ||
+ | vector min = llList2Vector(o, | ||
+ | vector max = llList2Vector(o, | ||
+ | vector pos = llList2Vector(llGetObjectDetails(O1, | ||
+ | vector o1_min = <pos.x + min.x, pos.y + min.y, pos.z + min.z>; | ||
+ | vector o1_max = <pos.x + max.x, pos.y + max.y, pos.z + max.z>; | ||
+ | o = llGetBoundingBox(O2); | ||
+ | min = llList2Vector(o, | ||
+ | max = llList2Vector(o, | ||
+ | pos = llList2Vector(llGetObjectDetails(O2, | ||
+ | vector o2_min = <pos.x + min.x, pos.y + min.y, pos.z + min.z>; | ||
+ | vector o2_max = <pos.x + max.x, pos.y + max.y, pos.z + max.z>; | ||
+ | //intersect | ||
+ | return (integer)( | ||
+ | ( | ||
+ | (o1_min.x == o2_min.x && o1_min.y == o2_min.y && o1_min.z == o2_min.z) && | ||
+ | (o1_max.x == o2_max.x && o1_max.y == o2_max.y && o1_max.z == o2_max.z) | ||
+ | ) || ( | ||
+ | (o1_min.x < o2_min.x && o1_min.y < o2_min.y && o1_min.z < o2_min.z) && | ||
+ | (o1_max.x < o2_max.x && o1_max.y < o2_max.y && o1_max.z < o2_max.z) && | ||
+ | (o1_min.x < o2_max.x && o1_min.y < o2_max.y && o1_max.z < o2_max.z) && | ||
+ | (o1_max.x > o2_min.x && o1_max.y > o2_min.y && o1_max.z > o2_min.z) | ||
+ | ) || ( | ||
+ | (o1_min.x > o2_min.x && o1_min.y < o2_min.y && o1_min.z < o2_min.z) && | ||
+ | (o1_max.x > o2_max.x && o1_max.y < o2_max.y && o1_max.z < o2_max.z) && | ||
+ | (o1_min.x < o2_max.x && o1_min.y < o2_max.y && o1_min.z < o2_max.z) && | ||
+ | (o1_max.x > o2_min.x && o1_max.y > o2_min.y && o1_max.z > o2_min.z) | ||
+ | ) || ( | ||
+ | (o1_min.x > o2_min.x && o1_min.y < o2_min.y && o1_min.z > o2_min.z) && | ||
+ | (o1_max.x > o2_max.x && o1_max.y < o2_max.y && o1_max.z > o2_max.z) && | ||
+ | (o1_min.x < o2_max.x && o1_min.y < o2_max.y && o1_min.z < o2_max.z) && | ||
+ | (o1_max.x > o2_min.x && o1_max.y > o2_min.y && o1_max.z > o2_min.z) | ||
+ | ) || ( | ||
+ | (o1_min.x < o2_min.x && o1_min.y < o2_min.y && o1_min.z > o2_min.z) && | ||
+ | (o1_max.x < o2_max.x && o1_max.y < o2_max.y && o1_max.z > o2_max.z) && | ||
+ | (o1_min.x < o2_max.x && o1_min.y < o2_max.y && o1_min.z < o2_max.z) && | ||
+ | (o1_max.x > o2_min.x && o1_max.y > o2_min.y && o1_max.z > o2_min.z) | ||
+ | ) || ( | ||
+ | (o1_min.x < o2_min.x && o1_min.y > o2_min.y && o1_min.z > o2_min.z) && | ||
+ | (o1_max.x < o2_max.x && o1_max.y > o2_max.y && o1_max.z > o2_max.z) && | ||
+ | (o1_min.x < o2_max.x && o1_min.y < o2_max.y && o1_min.z < o2_max.z) && | ||
+ | (o1_max.x > o2_min.x && o1_max.y > o2_min.y && o1_max.z > o2_min.z) | ||
+ | ) || ( | ||
+ | (o1_min.x > o2_min.x && o1_min.y > o2_min.y && o1_min.z > o2_min.z) && | ||
+ | (o1_max.x > o2_max.x && o1_max.y > o2_max.y && o1_max.z > o2_max.z) && | ||
+ | (o1_min.x < o2_max.x && o1_min.y < o2_max.y && o1_min.z < o2_max.z) && | ||
+ | (o1_max.x > o2_min.x && o1_max.y > o2_min.y && o1_max.z > o2_min.z) | ||
+ | ) || ( | ||
+ | (o1_min.x > o2_min.x && o1_min.y > o2_min.y && o1_min.z < o2_min.z) && | ||
+ | (o1_max.x > o2_max.x && o1_max.y > o2_max.y && o1_max.z < o2_max.z) && | ||
+ | (o1_min.x < o2_max.x && o1_min.y < o2_max.y && o1_min.z < o2_max.z) && | ||
+ | (o1_max.x > o2_min.x && o1_max.y > o2_min.y && o1_max.z > o2_min.z) | ||
+ | ) || ( | ||
+ | (o1_min.x < o2_min.x && o1_min.y > o2_min.y && o1_min.z < o2_min.z) && | ||
+ | (o1_max.x < o2_max.x && o1_max.y > o2_max.y && o1_max.z < o2_max.z) && | ||
+ | (o1_min.x < o2_max.x && o1_min.y < o2_max.y && o1_min.z < o2_max.z) && | ||
+ | (o1_max.x > o2_min.x && o1_max.y > o2_min.y && o1_max.z > o2_min.z) | ||
+ | ) | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | An example call of '' | ||
+ | |||
+ | <code lsl2> | ||
+ | default { | ||
+ | state_entry() { | ||
+ | key Oa = (key)" | ||
+ | key Ob = (key)" | ||
+ | if(wasObjectIntersect(Oa, | ||
+ | llOwnerSay(" | ||
+ | return; | ||
+ | } | ||
+ | llOwnerSay(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Determine the Bounding Box Points of an Object or Avatar ====== | ||
+ | |||
+ | Given an object '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // returns all points of the bounding box of object O | ||
+ | list wasBoundingPoints(key O) { | ||
+ | list bb = llGetBoundingBox(O); | ||
+ | vector min = llList2Vector(bb, | ||
+ | vector max = llList2Vector(bb, | ||
+ | vector pos = llList2Vector(llGetObjectDetails(O, | ||
+ | | ||
+ | return [ | ||
+ | <pos.x + min.x, pos.y + min.y, pos.z + min.z>, | ||
+ | <pos.x + max.x, pos.y + max.y, pos.z + min.z>, | ||
+ | <pos.x + min.x, pos.y + max.y, pos.z + min.z>, | ||
+ | <pos.x + max.x, pos.y + min.y, pos.z + min.z>, | ||
+ | <pos.x + min.x, pos.y + min.y, pos.z + max.z>, | ||
+ | <pos.x + max.x, pos.y + max.y, pos.z + max.z>, | ||
+ | <pos.x + min.x, pos.y + max.y, pos.z + max.z>, | ||
+ | <pos.x + max.x, pos.y + min.y, pos.z + max.z> | ||
+ | ]; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Determine the Bounding Box Sides of an Avatar or Object ====== | ||
+ | |||
+ | The function '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // returns all the sides of the bounding box | ||
+ | list wasBoundingSides(key O) { | ||
+ | |||
+ | list bb = llGetBoundingBox(O); | ||
+ | vector min = llList2Vector(bb, | ||
+ | vector max = llList2Vector(bb, | ||
+ | vector pos = llList2Vector(llGetObjectDetails(O, | ||
+ | | ||
+ | vector p1 = <pos.x + min.x, pos.y + min.y, pos.z + min.z>; | ||
+ | vector p2 = <pos.x + max.x, pos.y + max.y, pos.z + min.z>; | ||
+ | vector p3 = <pos.x + min.x, pos.y + max.y, pos.z + min.z>; | ||
+ | vector p4 = <pos.x + max.x, pos.y + min.y, pos.z + min.z>; | ||
+ | vector p5 = <pos.x + min.x, pos.y + min.y, pos.z + max.z>; | ||
+ | vector p6 = <pos.x + max.x, pos.y + max.y, pos.z + max.z>; | ||
+ | vector p7 = <pos.x + min.x, pos.y + max.y, pos.z + max.z>; | ||
+ | vector p8 = <pos.x + max.x, pos.y + min.y, pos.z + max.z>; | ||
+ | |||
+ | return [p1, p3, p1, p4, p1, p5, p2, p3, p2, p4, p2, p6, p3, p7, p4, p8, p5, p7, p5, p8, p6, p7, p6, p8]; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Determine if a Point falls within Rectangle Bounds ====== | ||
+ | |||
+ | Given a point $P$ and a rectangle bounded by '' | ||
+ | |||
+ | < | ||
+ | x_min, y_max +-----+ x_max, y_max | ||
+ | | ||
+ | | ||
+ | x_min, y_min +-----+ x_max, y_min | ||
+ | </ | ||
+ | |||
+ | the function returns true if the point $P$ is inside the rectangle. | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // returns true if the point p is within the rectangle bounds given by the | ||
+ | // supplied x{min,max} and y{min,max} components of the rectangle bounds. | ||
+ | integer wasPointInRectangle(vector p, | ||
+ | float x_min, float y_min, float x_max, float y_max, ) { | ||
+ | | ||
+ | return (integer)( | ||
+ | p.x < x_max && p.x > x_min && | ||
+ | p.y < y_max && p.y > y_min | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Determine if a Segment Intersects a Rectangle ====== | ||
+ | |||
+ | Using [[fuss: | ||
+ | |||
+ | The rectangle bounds '' | ||
+ | < | ||
+ | x_min, y_max +-----+ x_max, y_max | ||
+ | | ||
+ | | ||
+ | x_min, y_min +-----+ x_max, y_min | ||
+ | </ | ||
+ | |||
+ | A segment can intersect a rectangle in one or two points, however '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // returns true if the segment pq intersects the rectangle given by the | ||
+ | // supplied x{min,max} and y{min,max} components of the rectangle bounds. | ||
+ | integer wasSegmentIntersectRectangle(vector p, vector q, | ||
+ | float x_min, float y_min, float x_max, float y_max) { | ||
+ | // | ||
+ | // x_min, y_max +-----+ x_max, y_max | ||
+ | // | | | ||
+ | // | | | ||
+ | // x_min, y_min +-----+ x_max, y_min | ||
+ | // | ||
+ | return (integer)( | ||
+ | wasSegmentIntersect(< | ||
+ | <x_min, y_min, 0>, <x_min, y_max, 0>) || | ||
+ | wasSegmentIntersect(< | ||
+ | <x_min, y_min, 0>, <x_max, y_min, 0>) || | ||
+ | wasSegmentIntersect(< | ||
+ | <x_max, y_max, 0>, <x_min, y_max, 0>) || | ||
+ | wasSegmentIntersect(< | ||
+ | <x_max, y_max, 0>, <x_max, y_min, 0>) | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | An example call of the function is as follows: | ||
+ | <code lsl2> | ||
+ | default { | ||
+ | state_entry() { | ||
+ | vector A = <1, 1, 0>; | ||
+ | vector B = <0, 0, 0>; | ||
+ | if(wasSegmentIntersectRectangle(A, | ||
+ | llOwnerSay(" | ||
+ | return; | ||
+ | } | ||
+ | llOwnerSay(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Get Day of Week ====== | ||
+ | |||
+ | '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasDayOfWeek(integer year, integer month, integer day) { | ||
+ | return llList2String( | ||
+ | [ " | ||
+ | " | ||
+ | ( | ||
+ | day | ||
+ | + ((153 * (month + 12 * ((14 - month) / 12) - 3) + 2) / 5) | ||
+ | + (365 * (year + 4800 - ((14 - month) / 12))) | ||
+ | + ((year + 4800 - ((14 - month) / 12)) / 4) | ||
+ | - ((year + 4800 - ((14 - month) / 12)) / 100) | ||
+ | + ((year + 4800 - ((14 - month) / 12)) / 400) | ||
+ | - 32045 | ||
+ | ) % 7 | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | For example, the following call: | ||
+ | <code lsl2> | ||
+ | list stamp = llList2List( | ||
+ | llParseString2List( | ||
+ | llGetTimestamp(), | ||
+ | [" | ||
+ | ), | ||
+ | 0, 2); | ||
+ | llOwnerSay( | ||
+ | wasDayOfWeek( | ||
+ | llList2Integer(stamp, | ||
+ | llList2Integer(stamp, | ||
+ | llList2Integer(stamp, | ||
+ | ) | ||
+ | ); | ||
+ | </ | ||
+ | |||
+ | will print out the current day of week. | ||
+ | |||
+ | ====== Floating-Point Modulo ====== | ||
+ | |||
+ | Since LSL's modulo operator '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | float wasFmod(float a, float p) { | ||
+ | if(p == 0) return (float)" | ||
+ | return a - ((integer)(a/ | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | For example: | ||
+ | <code lsl2> | ||
+ | llOwnerSay((string)wasFmod(5.1, | ||
+ | </ | ||
+ | |||
+ | will print out '' | ||
+ | |||
+ | |||
+ | ====== Get Number of Days in a Month ====== | ||
+ | |||
+ | Given a month number and year number the '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasGetMonthDays(integer month, integer year) { | ||
+ | if (month == 4 || month == 6 || month == 9 || month == 11) { | ||
+ | return 30; | ||
+ | } | ||
+ | if(month == 2) { | ||
+ | integer leap = (year % 4 == 0 && year % 100 != 0) || | ||
+ | (year % 400 == 0); | ||
+ | if(leap == TRUE) { | ||
+ | return 29; | ||
+ | } | ||
+ | return 28; | ||
+ | } | ||
+ | return 31; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Get Number of Days in Year ====== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasGetYearDays(integer year) { | ||
+ | integer leap = (year % 4 == 0 && year % 100 != 0) || | ||
+ | (year % 400 == 0); | ||
+ | if(leap == TRUE) { | ||
+ | return 366; | ||
+ | } | ||
+ | return 365; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Unix Time to Stamp ====== | ||
+ | |||
+ | '' | ||
+ | |||
+ | < | ||
+ | YYYY-MM-DDTHH: | ||
+ | </ | ||
+ | |||
+ | The '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasUnixTimeToStamp(integer unix) { | ||
+ | integer year = 1970; | ||
+ | integer dayno = unix / 86400; | ||
+ | do { | ||
+ | dayno -= wasGetYearDays(year); | ||
+ | ++year; | ||
+ | } while (dayno >= wasGetYearDays(year)); | ||
+ | integer month = 1; | ||
+ | do { | ||
+ | dayno -= wasGetMonthDays(month, | ||
+ | ++month; | ||
+ | } while (dayno >= wasGetMonthDays(month, | ||
+ | return (string)year + " | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | An example call is: | ||
+ | <code lsl2> | ||
+ | llOwnerSay( | ||
+ | wasUnixTimeToStamp( | ||
+ | llGetUnixTime() - | ||
+ | ((integer) llGetGMTclock() - (integer) llGetWallclock()) | ||
+ | ) | ||
+ | ); | ||
+ | </ | ||
+ | |||
+ | which displays the current date and time in '' | ||
+ | ====== Get Bit Flags ====== | ||
+ | |||
+ | The function gets the flags in the '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasGetBitFlags(integer mask, list flags) { | ||
+ | list result = []; | ||
+ | integer i = llGetListLength(flags)-1; | ||
+ | do { | ||
+ | if(((integer)llPow(2, | ||
+ | result += llList2String(flags, | ||
+ | } while(--i> | ||
+ | return result; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | An example is the following: | ||
+ | <code lsl2> | ||
+ | llOwnerSay( | ||
+ | llDumpList2String( | ||
+ | wasGetBitFlags( | ||
+ | 75, | ||
+ | [ " | ||
+ | ), | ||
+ | "," | ||
+ | ) | ||
+ | ); | ||
+ | </ | ||
+ | |||
+ | The function would return: | ||
+ | < | ||
+ | Be Hired, | ||
+ | </ | ||
+ | |||
+ | ====== Set Bit Flags ====== | ||
+ | |||
+ | Returns a mask from the '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasSetBitFlags(list set, list flags) { | ||
+ | if(llGetListLength(set) == 0) return 0; | ||
+ | string flag = llList2String(set, | ||
+ | set = llDeleteSubList(set, | ||
+ | return (integer) | ||
+ | llPow( | ||
+ | 2, | ||
+ | llListFindList(flags, | ||
+ | ) | wasSetBitFlags(set, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | An example is issuing: | ||
+ | <code lsl2> | ||
+ | llOwnerSay((string) | ||
+ | wasSetBitFlags( | ||
+ | ["Be Hired"," | ||
+ | [ " | ||
+ | ) | ||
+ | ); | ||
+ | </ | ||
+ | |||
+ | which will return '' | ||
+ | |||
+ | ====== Replace all Sub-Strings or all Sub-List Elements ====== | ||
+ | |||
+ | Suppose that you have the string: '' | ||
+ | |||
+ | The following solution is recursive: | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasListReplace(list l, string s, string r) { | ||
+ | if(llGetListLength(l) == 0) return ""; | ||
+ | string q = llList2String(l, | ||
+ | l = llDeleteSubList(l, | ||
+ | if(q == s) return r + wasListReplace(l, | ||
+ | return q + wasListReplace(l, | ||
+ | } | ||
+ | |||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasReplaceSubString(string i, string s, string r) { | ||
+ | return wasListReplace(llParseString2List(i, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | an example call being: | ||
+ | <code lsl2> | ||
+ | llOwnerSay(wasReplaceSubString(" | ||
+ | </ | ||
+ | |||
+ | An iterative solution is the following: | ||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasReplaceSubString(string i, string s, string r) { | ||
+ | string result; | ||
+ | list l = llParseString2List(i, | ||
+ | do { | ||
+ | string q = llList2String(l, | ||
+ | if(q == s) { | ||
+ | result += r; | ||
+ | jump continue; | ||
+ | } | ||
+ | result += q; | ||
+ | @continue; | ||
+ | l = llDeleteSubList(l, | ||
+ | } while(llGetListLength(l) != 0); | ||
+ | return result; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ====== Permute List and String Elements ====== | ||
+ | |||
+ | Given a string or list of elements these functions permute the string or list a certain number of times in both forward and reverse directions. | ||
+ | |||
+ | ===== Reverse ===== | ||
+ | |||
+ | The '' | ||
+ | |||
+ | Given a list of string containing the elements: | ||
+ | < | ||
+ | a b c | ||
+ | </ | ||
+ | |||
+ | and calling either function with the '' | ||
+ | < | ||
+ | b c a | ||
+ | </ | ||
+ | |||
+ | ==== String ==== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasReversePermuteStringElements(string input, integer times) { | ||
+ | if(times == 0) return input; | ||
+ | return wasReversePermuteStringElements( | ||
+ | | ||
+ | --times | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== List ==== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasReversePermuteListElements(list input, integer times) { | ||
+ | if(times == 0) return input; | ||
+ | return wasReversePermuteListElements( | ||
+ | | ||
+ | --times | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Forward ===== | ||
+ | |||
+ | The '' | ||
+ | |||
+ | Given a list of string containing the elements: | ||
+ | < | ||
+ | a b c | ||
+ | </ | ||
+ | |||
+ | and calling either function with the '' | ||
+ | < | ||
+ | c a b | ||
+ | </ | ||
+ | |||
+ | ==== String ==== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | string wasForwardPermuteStringElements(string input, integer times) { | ||
+ | if(times == 0) return input; | ||
+ | return wasForwardPermuteStringElements( | ||
+ | | ||
+ | --times | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== List ==== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasForwardPermuteListElements(list input, integer times) { | ||
+ | if(times == 0) return input; | ||
+ | return wasForwardPermuteListElements( | ||
+ | | ||
+ | --times | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Permute a List To an Element ====== | ||
+ | |||
+ | Given an input list '' | ||
+ | <code lsl2> | ||
+ | list l = [ " | ||
+ | </ | ||
+ | |||
+ | calling '' | ||
+ | <code lsl2> | ||
+ | [ " | ||
+ | </ | ||
+ | |||
+ | The complexity of this algorithm is $O(1)$. | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasListPermuteToElement(list rot, string ring) { | ||
+ | if(ring == llList2String(rot, | ||
+ | integer i = llListFindList(rot, | ||
+ | return ring + | ||
+ | llList2List( | ||
+ | llDeleteSubList( | ||
+ | rot, | ||
+ | i, | ||
+ | i | ||
+ | ), | ||
+ | i, | ||
+ | -1 | ||
+ | ) + | ||
+ | llList2List( | ||
+ | llDeleteSubList( | ||
+ | rot, | ||
+ | i+1, | ||
+ | -1 | ||
+ | ), | ||
+ | 0, | ||
+ | i-1 | ||
+ | ); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Cryptographic Functions ====== | ||
+ | |||
+ | The following is a list of cyphers implemented in LSL by Wizardry and Steamworks: | ||
+ | |||
+ | {{indexmenu> | ||
+ | |||
+ | that can be used to encrypt and decrypt strings. | ||
+ | ====== Case-Insensitive String Compare ====== | ||
+ | |||
+ | The '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasCaseFoldStringCompare(string a, string b) { | ||
+ | if(llStringLength(a) != llStringLength(b)) | ||
+ | return FALSE; | ||
+ | if(a == "" | ||
+ | return TRUE; | ||
+ | | ||
+ | string A = llGetSubString(a, | ||
+ | a = llDeleteSubString(a, | ||
+ | string B = llGetSubString(b, | ||
+ | b = llDeleteSubString(b, | ||
+ | |||
+ | if(llToUpper(A) == llToUpper(B)) | ||
+ | | ||
+ | | ||
+ | return FALSE; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | An example call is the following: | ||
+ | <code lsl2> | ||
+ | wasCaseFoldStringCompare(" | ||
+ | </ | ||
+ | which will return '' | ||
+ | |||
+ | ====== Character Handling ====== | ||
+ | |||
+ | This set of functions operate on the first character of each string and are able to determine the type of character passed to these functions. | ||
+ | |||
+ | {{indexmenu> | ||
+ | |||
+ | ====== Fibonacci Number Generator ====== | ||
+ | |||
+ | The '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasFibonacci(integer count, integer first, integer second) { | ||
+ | if(count <= 0) return []; | ||
+ | return first + wasFibonacci(--count, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | For example, to generate the first '' | ||
+ | <code lsl2> | ||
+ | llOwnerSay(llDumpList2String(wasFibonacci(10, | ||
+ | </ | ||
+ | |||
+ | which will print out: | ||
+ | < | ||
+ | [01: | ||
+ | </ | ||
+ | |||
+ | ====== Check if Avatar is in Sensor Range ====== | ||
+ | |||
+ | The '' | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | integer wasIsAvatarInSensorRange(key avatar) { | ||
+ | return llListFindList( | ||
+ | llGetAgentList( | ||
+ | AGENT_LIST_REGION, | ||
+ | [] | ||
+ | ), | ||
+ | (list)((key)avatar) | ||
+ | ) != -1 && | ||
+ | llVecDist( | ||
+ | llGetPos(), | ||
+ | llList2Vector( | ||
+ | llGetObjectDetails( | ||
+ | avatar, | ||
+ | [OBJECT_POS] | ||
+ | ), | ||
+ | 0 | ||
+ | ) | ||
+ | ) <= 96; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The judgement is made first on the premises that the specified '' | ||
+ | |||
+ | ====== Smallest and Largest Segment ====== | ||
+ | |||
+ | Given a set of points, the following algorithms determine the smallest, respectively largest segment between the points. | ||
+ | |||
+ | ===== Smallest ===== | ||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasSmallestSegment(list points) { | ||
+ | list dists = []; | ||
+ | list xi = []; | ||
+ | list xj = []; | ||
+ | integer i = llGetListLength(points) - 1; | ||
+ | do { | ||
+ | integer j = llGetListLength(points) - 1; | ||
+ | do { | ||
+ | if(i == j) jump continue; | ||
+ | float d = llVecDist( | ||
+ | llList2Vector(points, | ||
+ | llList2Vector(points, | ||
+ | ); | ||
+ | dists += d; | ||
+ | xi += i; | ||
+ | xj += j; | ||
+ | @continue; | ||
+ | } while(--j > -1); | ||
+ | } while(--i > -1); | ||
+ | | ||
+ | i = llGetListLength(dists) - 1; | ||
+ | float d = llList2Float(dists, | ||
+ | --i; | ||
+ | integer x = 0; | ||
+ | integer y = 0; | ||
+ | do { | ||
+ | float t = llList2Float(dists, | ||
+ | if(d > t) { | ||
+ | x = llList2Integer(xi, | ||
+ | y = llList2Integer(xj, | ||
+ | d = t; | ||
+ | } | ||
+ | } while(--i > -1); | ||
+ | | ||
+ | return [llList2Vector(points, | ||
+ | } | ||
+ | </ | ||
+ | ===== Largest ===== | ||
+ | |||
+ | <code lsl2> | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // | ||
+ | /////////////////////////////////////////////////////////////////////////// | ||
+ | list wasLargestSegment(list points) { | ||
+ | list dists = []; | ||
+ | list xi = []; | ||
+ | list xj = []; | ||
+ | integer i = llGetListLength(points) - 1; | ||
+ | do { | ||
+ | integer j = llGetListLength(points) - 1; | ||
+ | do { | ||
+ | if(i == j) jump continue; | ||
+ | float d = llVecDist( | ||
+ | llList2Vector(points, | ||
+ | llList2Vector(points, | ||
+ | ); | ||
+ | dists += d; | ||
+ | xi += i; | ||
+ | xj += j; | ||
+ | @continue; | ||
+ | } while(--j > -1); | ||
+ | } while(--i > -1); | ||
+ | | ||
+ | i = llGetListLength(dists) - 1; | ||
+ | float d = llList2Float(dists, | ||
+ | --i; | ||
+ | integer x = 0; | ||
+ | integer y = 0; | ||
+ | do { | ||
+ | float t = llList2Float(dists, | ||
+ | if(d < t) { | ||
+ | x = llList2Integer(xi, | ||
+ | y = llList2Integer(xj, | ||
+ | d = t; | ||
+ | } | ||
+ | } while(--i > -1); | ||
+ | | ||
+ | return [llList2Vector(points, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== Inline Minimum of n-Values ====== | ||
+ | |||
+ | For two values (originally by Hermit Barber): | ||
+ | <code lsl2> | ||
+ | return llAbs(x >= y) * y + | ||
+ | llAbs(x < y) * x; | ||
+ | </ | ||
+ | |||
+ | This relies on the principle that '' | ||
+ | |||
+ | Inducing to three values: | ||
+ | <code lsl2> | ||
+ | return llAbs((llAbs(x >= y) * y) > z) * z + | ||
+ | llAbs((llAbs(y > x) * x) >= z) * z + | ||
+ | llAbs((llAbs(z >= x) * x) > y) * y + | ||
+ | llAbs((llAbs(x > z) * z) >= y) * y + | ||
+ | llAbs((llAbs(y >= z) * z) > x) * x + | ||
+ | llAbs((llAbs(z > y) * y) >= x) * x | | ||
+ | // Base case where all values are equal x == y == z | ||
+ | llAbs((llAbs(x == y) * x) == z) * x; | ||
+ | | ||
+ | </ | ||
+ | |||
+ | The trick to the induction is to generate all possible permutations of $n$ variables and expand on the pattern whilst keeping the base-case where all values are equal. The base-case must be kept because the placement of $\ge$ ensures that when all three values are equal, the result is $0$ such that the logical $OR$ takes care to turn the result into the value of any variable (in this case, the value of $x$). | ||
+ | |||
+ | ====== Ternary Operator ====== | ||
+ | |||
+ | LSL does not have a ternary operator but a one-line branching decision can be expressed with: | ||
+ | <code lsl2> | ||
+ | string a = llList2String([ " | ||
+ | </ | ||
+ | where: | ||
+ | * '' | ||
+ | |||
+ | such that '' | ||
+ | |||
+ | Remarkably, the expression preserves the same type constraints that a ternary operator would have: the type of '' | ||
+ | |||