Open Prim Animator/Frame Labels

From Second Life Wiki
Jump to navigation Jump to search
//!  @brief Provides an API to allow animation frames to be "labelled" so they can be called by string instead of integer.
/**
*    @author SignpostMarv Martin
*/

getRecordedSnapshots(){ // short-hand for getting the number of animation frames
    llMessageLinked(LINK_THIS,-1,"XDrecordedSnapshots",NULL_KEY);
}

//!  name of the notecard to load the frame labels from. Each label should be on a new line corresponding to the animation frame (first line first frame, second line second frame and so on)
string notecard = "OPA-Frame-Labels";

//!  notecard contents
string contents;

//!  notecard lines
list   _contents;

//!  dataserver keys for non-sequential loading of notecard lines.
list multithread_fetch = [];

//!  dataserver key for fetching the number of notecard lines
key nlq;

//!  Yet another vestigial copypasta.
export_string(string _export_string){
    integer s=1000;
    while(llStringLength(_export_string) > s){
        llOwnerSay(llGetSubString(_export_string,0,(s-1)));
        _export_string = llGetSubString(_export_string,s,-1);
    }
    if(llStringLength(_export_string) > 0){
        llOwnerSay(_export_string);
    }
}

//!  number of animation frames
integer recordedSnapshots = 0;

//!  command to change the current animation frame.
key op_change_frame = "1071f377-8623-4ed3-acc1-d30cabb4524a";

default{
    state_entry(){
        integer type = llGetInventoryType(notecard); // check the notecard is in inventory
        if(type == INVENTORY_NONE){ // if type is INVENTORY_NONE it isn't there.
            llOwnerSay("Notecard not found");
        }else if(type != INVENTORY_NOTECARD){ // if type is not INVENTORY_NOTECARD, something else is hogging the name.
            llOwnerSay("Inventory item found with name of notecard, but it is not a notecard");
        }else{ // notecard found, proceeding with parsing process
            state num_lines;
        }
    }

    changed(integer c){ // rather than resetting the script, we jump straight to the num_lines state if the notecard is now found.
        if(c & CHANGED_INVENTORY && llGetInventoryType(notecard) == INVENTORY_NOTECARD){
            state num_lines;
        }
    }

//    state_exit(){
//        llOwnerSay("Notecard found, parsing");
//    }
}

state num_lines{ // we're using separate states to avoid race conditions with changed inventory etc.
    state_entry(){
        nlq = llGetNumberOfNotecardLines(notecard); // fire off a request for the notecard lines
    }

    dataserver(key q, string m){
        if(q == nlq){
            integer i;
            integer j = (integer)m;
            if(j < 1){
                contents = "";
                state done_reading;
            }
            list    k = []; // declaring list k = []; isn't exactly necessary, you could do list k;
            list    l = [];
            for(i=0;i<j;++i){ // since we don't have the concept of transactions in LSL scripts, we're going to build up these lists separately 
                k += [NULL_KEY];
                l += [NULL_KEY];
            }
            _contents = k; // this is where we do the atomic writes due to lack of transactions on variables
            multithread_fetch = l; // this is where we do the atomic writes due to lack of transactions on variables
            k = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
            l = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
            state mtf;
        }
    }
}

state mtf{ // fetch all notecard lines without the delay caused by traditional sequential loading.
    state_entry(){
        integer i;
        integer j=llGetListLength(multithread_fetch);
        for(i=0;i<j;++i){
            multithread_fetch = llListReplaceList(multithread_fetch,[llGetNotecardLine(notecard,i)],i,i); // sets the notecard fetching away
        }
    }

    dataserver(key q, string m){
        integer i = llListFindList(multithread_fetch,[q]);
        if(i >= 0){
            _contents = llListReplaceList(_contents,[m],i,i); // the traditional method of notecard fetching in LSL is single-threaded, getting one line at a time
        }
        if(llListFindList(_contents,[NULL_KEY]) == -1){ // since we started out with a NULLL_KEY-filled list, if there aren't any NUL_KEY in the list then we know it's done. caveat scriptor: since key and string types are interchangeable 
            contents = llDumpList2String(_contents,"\n");
            state done_reading;
        }
    }

    state_exit(){
        _contents = [];
        multithread_fetch = [];
    }
}

state done_reading{
    state_entry(){
        _contents = llParseString2List(contents, ["\n"],[]);
//        llOwnerSay((string)llGetListLength(_contents) + " frame labels found");
        getRecordedSnapshots();
    }

    link_message(integer s, integer n, string m, key i){
        if(m == "XDrecordedSnapshots" && n >= 0){ // if we're updating the number of animation frames
            recordedSnapshots = n;
            if(recordedSnapshots == 1){ // and there's only one frame
                llMessageLinked(LINK_THIS,0,"XDplay",NULL_KEY); // play it.
            }
        }else if(i == op_change_frame){ // if we receive a command to change to an animation frame by a named label,
            integer frame = llListFindList(_contents,[m]); // check the label exists in the notecard contents
            if(frame >= 0){ // then if the named label does exist,
                llMessageLinked(LINK_THIS,frame + 1,"XDshow",NULL_KEY); // show it.
            }
        }
    }

    changed(integer c){
        if(c & CHANGED_INVENTORY){
            llResetScript();
        }
    }
}