Difference between revisions of "Wizardry and Steamworks"

From Second Life Wiki
Jump to navigation Jump to search
m
(Redirected page to Category:Wizardry and Steamworks)
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
[[File:WaS-logo-landscape.png|400px|center|Wizardry and Steamworks Logo Landscape]]
#REDIRECT [[:Category:Wizardry and Steamworks]]
 
=Wizardry and Steamworks=
 
Wizardry and Steamworks are a development group to be found in Second Life. The leaders of the group are [[User:Kira Komarov|Kira Komarov]] and [[User:Flax Quirina|Flax Quirina]]. We specialize on scripting in Second Life, however anything that pushes the boundaries of what is currently possible in Second Life is of interest to us. This is our legacy.
 
=Member Wiki Mirrors=
 
* [http://eva.comaroski.me/wiki/Wizardry_and_Steamworks WaS-K] - Kira Komarov
 
=FUS (Frequently Used Snippets)=
 
Frequently Used Snippets (FUS) are short snippets that are generic enough to be included in any script. Just as you have generic types, generic functions, these represent an abstraction over Second Life script programming methodologies particular to Wizardry and Steamworks [WaS] and although they aren't delivered in the form of scripts, they can be used in any script by inserting them at the right places.
 
==Private Channel Key-Hash==
 
Extremely useful, taken from [[llDialog]].
 
'''Inline Usage'''
<lsl>
integer comChannel = ((integer)("0x"+llGetSubString((string) /* llGetOwner(), llGetCreator(), llDetectedKey(0), id */,-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
</lsl>
==Endless Menu with Back and Next==
 
This generates an endless menu with Back and Next buttons by arranging a list dynamically whenever the Back and Next buttons are pressed. It has been used in several scripts with some variations.
 
'''Global Primitives'''
<lsl>
list menu_items = [];
list key_items = [];
integer mitra = 0;
list cList = []
</lsl>
'''Global Functions'''
<lsl>
list mFwd() {
    if(mitra+1>llGetListLength(menu_items)) return cList;
    cList = llListInsertList(llListInsertList(llList2List(menu_items, ++mitra, (mitra+=9)), ["<= Back"], 0), ["Next =>"], 2);
    return cList;
}
list mBwd() {
    if(mitra-19<0) return cList;
    cList = llListInsertList(llListInsertList(llList2List(menu_items, (mitra-=19), (mitra+=9)), ["<= Back"], 0), ["Next =>"], 2);
    return cList;
}
</lsl>
'''Inline Usage'''
<lsl>
            integer itra;
            for(itra=0, mitra=0, menu_items=[]; itra< llGetListLength(/* list */); ++itra) {
                menu_items += llList2String(/* list */, itra);
            }
            cList = llListInsertList(llListInsertList(llList2List(menu_items, mitra, (mitra+=9)), ["<= Back"], 0), ["Next =>"], 2);
            llDialog(/* Key to prompt */, /* Dialog Text */, cList, /* channel */);
</lsl>
 
==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.
 
'''Global Primitives'''
<lsl>
key nQuery = NULL_KEY;
integer nLine = 0;
list nList = [];
//pragma inline
string nName = "New Notecard";
</lsl>
'''Reading'''
<lsl>
        integer itra;
        for(itra=0. accessList=[]; itra<llGetInventoryNumber(INVENTORY_NOTECARD); ++itra) {
            if(llGetInventoryName(INVENTORY_NOTECARD, itra) == nName)
                jump found_notecard;
        }
        llInstantMessage(llGetOwner(), "Failed to find notecard.");
        return;
@found_notecard;
        nQuery = llGetNotecardLine(nName, nLine);
</lsl>
'''Dataserver'''
<lsl>
    dataserver(key id, string data) {
        if(id != nQuery) return;
        if(data == EOF) return;
        if(data == "") jump next_line;
        list nList += data;
@next_line;
        nQuery = llGetNotecardLine(nName, ++nLine);
    }
</lsl>
'''Extension: Return if key/name NOT in nList'''
<lsl>
        if(!~llListFindList(nList, (list)/* llDetectedKey(0), llDetectedName(0) */)) return;
</lsl>
 
==Side-By-Side Dual List Enumeration==
 
Enumerates two lists side-by side, separating the elements by a separator.
 
'''Global primitives'''
<lsl>
list list_a = [ /* contents */ ];
list lib_b = [ /* contents */ ];
</lsl>
'''Inline usage'''
<lsl>
            llOwnerSay("--------------- START DUMP ---------------");
            for(itra=0; itra<llGetListLength(list_a); ++itra) {
                llOwnerSay(llList2String(list_a, itra) + " /* separator */ " + llList2String(list_b, itra));
                llSleep(llGetRegionTimeDilation());
            }
            llOwnerSay("---------------- END DUMP ----------------");
</lsl>
 
==Map Preserving Sort using Quicksort==
 
Although [[llListSort]] sorts individual lists, there may be cases where you would want to sort two lists so that their mappings remain stable.
 
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:
<pre>
f -> 10
a -> 37
d -> 1
e -> 4
b -> 2
c -> 3
z -> 1
</pre>
 
and we want to sort list_a lexicographically while preserving the mapping from letters to numbers above. The expected result should be:
 
<pre>
a -> 37
b -> 2
c -> 3
d -> 1
e -> 4
f -> 10
z -> 1
</pre>
 
<lsl>
//////////////////////////////////////////////////////////
// [WaS-K] Kira Komarov - 2011, License: GPLv3          //
// Please see: http://www.gnu.org/licenses/gpl.html    //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
list list_a = ["f", "a", "d", "e", "b", "c", "z"];
list list_b = [ 10, 37,  1,  4,  2,  3 ,  1 ];
 
integer stringComparer(list a, list b) {
    list alph = [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" ];
    integer i;
    for(i=0; i<llStringLength(llList2String(a, 0)) && i<llStringLength(llList2String(b, 0)); i++) {
        if(llListFindList(alph, (list)llGetSubString(llList2String(a, 0), i, i)) < llListFindList(alph, (list)llGetSubString(llList2String(b, 0), i, i))) {
            return TRUE;
        }
        if(llListFindList(alph, (list)llGetSubString(llList2String(a, 0), i, i)) > llListFindList(alph, (list)llGetSubString(llList2String(b, 0), i, i))) {
            return FALSE;
        }
    }
    return TRUE;
}
list quicksort(list a, list b) {
    if(llGetListLength(a) <= 1) return a+b;
    list pivot_a = llList2List(a, llGetListLength(a)/2, llGetListLength(a)/2);
    list pivot_b = llList2List(b, llGetListLength(b)/2, llGetListLength(b)/2);
 
    a = llDeleteSubList(a, llGetListLength(a)/2, llGetListLength(a)/2);
    b = llDeleteSubList(b, llGetListLength(b)/2, llGetListLength(b)/2);
    list less = [];
    list less_b = [];
    list more = [];
    list more_b = [];
    integer i;
    for(i=0; i<llGetListLength(a)*2; i++) {
        if(stringComparer(llList2List(a, i, i), pivot_a) == TRUE)
        {
            less += llList2List(a, i, i);
            less_b += llList2List(b, i, i);
        }
        else
        {
            more += llList2List(a, i, i);
            more_b += llList2List(b, i, i);
        }
    }
    return  quicksort(less, less_b) + (list)pivot_a + (list)pivot_b + quicksort(more, more_b);
}
default
{
    state_entry()
    {
        llOwnerSay("Dual - Quicksort list contains in order: " + llDumpList2String(quicksort(list_a, list_b), " "));
    }
}
</lsl>
 
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 followed by the elements of the second set:
 
<pre>
Object: Dual - Quicksort list contains in order: a 37 b 2 c 3 d 1 e 4 f 10 z 1
</pre>
 
This is a symmetry-based variation of [[Quicksort]].
 
Concerning practical applications, this would be great to sort a (NAME x KEY) set pair; for example displaying avatar names alphabetically while preserving their corresponding key order.
 
==RLV: Wearables & Attachment Lists as Strings==
 
A pain to type.
 
'''Local preferred, global if needed'''
<lsl>
//pragma inline
list CLOTHES = [ "gloves", "jacket", "pants", "shirt", "shoes", "skirt", "socks", "underpants", "undershirt" ];
//pragma inline
list ATTACHMENTS = [ "none", "chest", "skull", "left shoulder", "right shoulder", "left hand", "right hand",
                    "left foot", "right foot", "spine", "pelvis", "mouth", "chin", "left ear", "right ear",
                    "left eyeball", "right eyeball", "nose", "r upper arm", "r forearm", "l upper arm",
                    "l forearm", "right hip", "r upper leg", "r lower leg", "left hip", "l upper leg",
                    "l lower leg", "stomach", "left pec", "right pec", "center 2", "top right", "top",
                    "top left", "center", "bottom left", "bottom", "bottom right" ];
</lsl>
 
==RLV: Rotate Avatar To Face Target==
 
'''Primitives'''
<lsl>
vector targetpos = < /* x */, /* y */, /* z */ >;
</lsl>
'''Inline usage'''
<lsl>
        vector pointTo = targetpos - llGetPos();
        float angleTo = llAtan2(pointTo.x, pointTo.y);
        llOwnerSay("@setrot:" + (string)angleTo + "=force")
</lsl>
 
==Using [[llSensorRepeat]] as a Timer==
 
Can be used as a second [[timer]] event. Explained in [[User:Kira Komarov/Trick or Treat]].
 
'''Global primitives''';
<lsl>
integer flag = 0;
</lsl>
'''Starting the sensor-timer'''
<lsl>
        llSensorRepeat("", NULL_KEY, flag=1, 0.1, 0.1, 60);
</lsl>
'''(No) Sensor'''
<lsl>
    no_sensor() {
        if(flag) {
            --flag;
            return;
        }
 
        /* this area will be hit 60
          * seconds after the llSensorRepeat()
          * has been executed. */
    }
</lsl>
 
==Planar or Grid Distance==
 
[[File:WaS_Planar_Distance_Kitty_and_Book.png|400px|center|thumb|Distance is marked with red]]
 
The x,y planar distance between the kitty and the book is given by:
<lsl>
float length_of_red_primitive=llVecDist(<kitty.x,kitty.y,0>,<book.x,book.y,0>);
</lsl>
 
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==
 
[[File:WaS_Kitty_Rotates_to_Face_Book.png|400px|center|thumb|The rotation required to be applied to the kitty object in order to face the book.]]
 
The rotation is given by qΔ.
<lsl>
vector book_position = <83.889717, 92.310814, 500.5>;
vector kitty_position = <82.306671, 92.310814, 501.714783>;
 
default {
    state_entry() {
        rotation qΔ = llRotBetween(<1,0,0>,llVecNorm(<book_position.x-kitty_position.x,
                                                      book_position.y-kitty_position.y,
                                                      book_position.z-kitty_position.z>));
        llSetRot(llGetRot() * qΔ);
    }
}
</lsl>
By eliminating a component x, y or z in [[llVecNorm]] the kitty may turn on only on certain axes.
 
==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 at the [[Artillery]] article.
 
'''Global primitives'''
<lsl>
vector target = < /* x */, /* y */, /* z */ >;
float velocity = /* some velocity */;
</lsl>
'''Inline usage'''
<lsl>
        vector origin = llGetPos();
        dΔ=llVecDist(<target.x,target.y,0>,<origin.x,origin.y,0>);
        valSin = 9.81*dΔ/llPow(velocity, 2); /* Attempt high angle. */
        if(valSin < -1 || valSin > 1) valSin = 9.81/llPow(velocity, 2); /* Attempt low angle. */
        if(valSin < -1 || valSin > 1) return; /* Won't even lift the object off the ground. Abort. */
        llSetVelocity(llVecNorm(<1,0,0>*llRotBetween(<1,0,0>,llVecNorm(<target.x-origin.x,target.y-origin.y, dΔ*llTan((90-RAD_TO_DEG*llAsin(valSin)/2) * DEG_TO_RAD) +
                                                            llFabs(target.z-origin.z)>)))*velocity, FALSE);
</lsl>
 
==Pass Data to a Rezzed Object==
 
This will allow you to avoid using [[llRegionSay]], [[llWhisper]], [[llSay]] to pass data to a newly rezzed object. Meant for setting up private [[listen]] channels between the newly rezzed object and the rezzer by using the [[llDialog]] key hash method.
 
'''Hasher'''
<lsl>
integer Key2Number(key objKey) {
  return ((integer)("0x"+llGetSubString((string)objKey,-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
}
</lsl>
'''Inline usage'''
<lsl>
llRezObject("object name", /* params */,Key2Number(some key));
</lsl>
'''Rezzed object event'''
<lsl>
default
{
    on_rez(integer param) {
        /* param contains the passed data */
    }
}
</lsl>
 
==Round-Robin Link-Message Scheduler==
 
Given a circular linked-list with 3 nodes, we can build a round-robin scheduler that could serve for some cyclic synchronisation source.
 
With three nodes, the list can be represented as:
 
<pre>
[Robin_1] -> [Robin_2] -> [Robin_3]
      ^                    |
      +---------------------
</pre>
 
'''Robin_1'''
<lsl>
//////////////////////////////////////////////////////////
// [WaS-K] Kira Komarov - 2011, License: GPLv3          //
// Please see: http://www.gnu.org/licenses/gpl.html    //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
// Robin_1 -> Robin_2 -> Robin_3 -> Robin_1 -> ...
 
default
{
    touch_start(integer num)
    {
        // Begin.
        llMessageLinked(LINK_THIS, 1, llGetScriptName() + ": sync", "");
    }
   
    link_message(integer sender_num, integer num, string str, key id)
    {
        if(num!=1) return;
        llOwnerSay(str);
        llMessageLinked(LINK_THIS, 2, llGetScriptName() + ": sync", "");
    }
}
</lsl>
 
'''Robin_2'''
<lsl>
//////////////////////////////////////////////////////////
// [WaS-K] Kira Komarov - 2011, License: GPLv3          //
// Please see: http://www.gnu.org/licenses/gpl.html    //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
// Robin_1 -> Robin_2 -> Robin_3 -> Robin_1 -> ...
 
default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if(num!=2) return;
        llOwnerSay(str);
        llMessageLinked(LINK_THIS, 3, llGetScriptName() + ": sync", "");
    }
}
</lsl>
 
'''Robin_3'''
<lsl>
//////////////////////////////////////////////////////////
// [WaS-K] Kira Komarov - 2011, License: GPLv3          //
// Please see: http://www.gnu.org/licenses/gpl.html    //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
// Robin_1 -> Robin_2 -> Robin_3 -> Robin_1 -> ...
 
default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if(num!=3) return;
        llOwnerSay(str);
        llMessageLinked(LINK_THIS, 1, llGetScriptName() + ": sync", "");
    }
}
</lsl>
 
this implementation schedules three threads in a round-robin fashion based on three scripts in the same primitive. Other implementations may pass the clock using the link number directly instead of broadcasting.
 
Connections to other domains:
 
* Simulation of harmonic functions (based on oscillations between threads exchanging pallets, general oscillations).
* Multivibrators (multiphase) (when one thread has the pallet, the other does not).
* Domino logic (each script represents a gate, a number is passed through the gates and a number is incremented each time achieving the cascading effect).
 
... you get the picture.
 
==Data Persistency: Textures==
 
This would allow you to store a string message by forcing a texture to an UUID which would resist log-in and log-out. Created initially a very long time ago for somebody and used much later on with the elven ears created by [[User:Kira Komarov|Kira Komarov]]. The possible number of messages to store is equal to the number of faces.
 
[Might not work anymore, needs checking.]
 
* To store a message:  
<lsl>
llMessageLinked(/* link number the memory module script is in */, /* face number to store on */, /* message to store */, "@push");
</lsl>
 
* To retrieve a message:
<lsl>
llMessageLinked(/* link number the memory module script is in */, /* face number to retrieve the message from */, "", "@pull");
</lsl>
 
'''Memory Module Script'''
<lsl>
//////////////////////////////////////////////////////////
// [K] Kira Komarov - 2011, License: GPLv3              //
// Please see: http://www.gnu.org/licenses/gpl.html    //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
list cSelect(integer hex) {
if(hex) return ["20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f","50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f","60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f","70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e"];
return [" ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/","0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","[","]","^","_","`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","{","|","}","~"];
}
string AsciiToKey(string str) {
    integer itra;
    string hexStr;
    for(itra=0; itra<llStringLength(str); itra++) {
        hexStr += llList2String(cSelect(1), llListFindList(cSelect(0), (list)llGetSubString(str, itra, itra)));
    }
    while(itra<32) {
        hexStr += "0";
        itra++;
    }
    return llGetSubString(hexStr,0,7) + "-" + llGetSubString(hexStr,8,11)+ "-" + llGetSubString(hexStr,12,15)+ "-" + llGetSubString(hexStr,16,19)+ "-" + llGetSubString(hexStr,20,31);
}
string KeyToAscii(key k) {
integer itra;
string strKey;
for(itra=0; itra<llStringLength(k); itra++) {
if(llGetSubString(k, itra, itra) != "-")
strKey += llGetSubString(k, itra, itra);
}
string pureKey;
for(itra=0; itra<llStringLength(strKey); itra+=2) {
if(llGetSubString(strKey, itra, itra+1) != "00")
pureKey += llGetSubString(strKey, itra, itra+1);
}
string asciiStr;
for(itra=0; itra<llStringLength(pureKey); itra+=2) {
asciiStr += llList2String(cSelect(0), llListFindList(cSelect(1), (list)llGetSubString(pureKey, itra, itra+1)));
}
return asciiStr;
}
default {
    state_entry() {
llSetPrimitiveParams([PRIM_TYPE, PRIM_TYPE_BOX,
                            PRIM_HOLE_DEFAULT,  // hole_shape
                            <0.00, 1.0, 0.0>,  // cut
                            0.0,                // hollow
                            <0.0, 0.0, 0.0>,    // twist
                            <1.0, 1.0, 0.0>,    // top_size
                            <0.0, 0.0, 0.0>,    // top_Shear
                            PRIM_SIZE,
                            <0.01, 0.01, 0.01>,  // size
                            PRIM_COLOR,
                            ALL_SIDES,
                            <0.0, 0.0, 0.0>,
                            0.00
                      ]);
        llSetObjectName(llGetKey());
    }
    link_message(integer sender_num, integer num, string str, key id) {
    if(num < 0 || num > 6 || llStringLength(str) > 16) return;
    if(id == "@push") {
    llSetTexture((key)AsciiToKey(str), num);
    jump stored;
    }
    llMessageLinked(LINK_ALL_OTHERS, num, KeyToAscii(llGetTexture(num)), "@pull");
@stored;
    }
}
</lsl>
 
=Resources=
 
All this work should be attributed to the '''Wizardry and Steamworks''' group under the [http://www.gnu.org/copyleft/gpl.html GPLv3 license]. You are free to use these resources and even commercialize the products using them as long as you attribute the components you got from here to the '''Wizardry and Steamworks [WaS]''' group.
 
These resources are originals, in the sense that they have been made, ground-up by Wizardry and Steamworks members.
 
==Animations==
 
{| cellpadding="10" cellspacing="0" border="1"
|- style="background-color:#ffffcc; font-weight:bold;"
|File
|Description
|-
|[http://eva.comaroski.me/wiki/File:WaS_Lighter_-_Two_Hands.bvh WaS Lighter Two_Hands.bvh‎]
|The avatar leans forward and places the left mouth (holding a cigarette) and the right hand over and in front of the left mouth (holding a lighter).
|-
|[http://eva.comaroski.me/w/images/4/40/WaS-K_AnkleLock_-_Animation.bvh WaS-K AnkleLock Animation]
|Running this animation will lock your shoes to your ankles so they do not appear strange when being animated. See [[AnkleLock]].
|-
|Butter
|Ice cream
|}
 
==Sounds==
 
{| cellpadding="10" cellspacing="0" border="1"
|- style="background-color:#ffffcc; font-weight:bold;"
|File
|Length
|Description
|Key
|-
|[http://eva.comaroski.me/wiki/File:WaS_Lighter_-_Two_Hands.bvh WaS Zippo Short Flick Spin Click.wav]
|00:01s
|Zippo cap flicks open, the wheel is spun once, the zippo cap clicks shut.
|bc1b20c7-fc34-6df2-2296-64be51390be2
|-
|Bread
|Pie
|Bread
|Pie
|-
|Butter
|Ice cream
|Bread
|Pie
|}
 
=Projects=
 
* [[Kira Warp Core Drive]]
* [[N2K]]

Revision as of 00:29, 24 November 2011