This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm whether you accept or reject these cookies being set.

A cookie will be stored in your browser regardless of choice to prevent you being asked this question again. You will be able to change your cookie settings at any time using the link in the footer.

PMAC POSER V2.8 BASISSCRIPT TEIL1
#1
PMAC POSER V2.8 BASISSCRIPT TEIL1


Dieser Artikel enthält meine Weiterentwicklung des PMAC Posers. Ein Fork der Version 2.0

Das Script kann direkt in vorhandenen alten PMAC Poser bis 2.2 zum Austausch verwendet werden.

Was sind die Haupt Änderungen?

NPCs: NPC Rezzt nun im Gruppenkontext des Prims - damit NPCS auch auf Sims mit ausschließlich Gruppenzugang funktionieren.
Somit können NPCs auch auf gesperrten Sims eingesetzt werden, und fliegen auch nicht heraus wenn man später die Sim sperrt.

NPCs: Unterstützung für NPC Profil Foto. Fotos müssen ".bild npcname" heissen damit sie als Profilfoto im NPC Profil geladen werden.
Beispiel: die NPC Notecard heisst: ".NPC192 Donna Orphelia" so muss das Profilfoto ".bild Donna Ophelia" benannt werden.

NPCs: Unterstützung für NPC Profil Info. Notecard muss ".info npcname" heissen, damit deren Inhalt im Info Feld des Profils angezeigt wird.
Beispiel: die NPC Notecard heisst: ".NPC192 Donna Orphelia" so muss die Profilinfo ".info Donna Ophelia" benannt werden.
Die Notizkarte dafür erstellt man inworld. Leider ist die Textlänge im Simulator sehr beschränkt. Der Inhalt der Notizkare wird dann im Profil Info Textfeld dargestellt.

Menü Rechte Änderung: NPC Tänzer können nun von jedem Nutzer geholt werden
Es besteht nun die Möglichkeit vorzugeben wer auf dem Poser Rechte hat Posereigene NPCs zu laden oder zu löschen.
dafür werden folgende Variablen verwendet:
publicaccess   { zugang = TRUE;}                    Öffentlicher Zugang
groupaccess   { zugang = TRUE;}                    Gruppen Zugang
zugangsliste                                                      Persönlicher Zugang über UUID Liste
der Poserbesitzer hat immer Zugang
Ihr findet die Einstellmöglichkeit im Script:
list    zugangsliste = ["b6a520ac-a468-4bc3-9e52-b509e88a8bce","18da7264-3260-4b63-8173-2a6fd7e93e33","798a6b1e-2186-4cf2-b733-460cb7132ae9"]; // UUIDS berechtigter AVAs
integer groupaccess = FALSE;  // TRUE/FALSE - darf die Gruppe steuern?
integer publicaccess = FALSE; // TRUE/FALSE - darf jeder steuern?

Admin Rechtestruktur erweitert vom Owner auf Berechtigte

SIMCROSSING Fähigkeit: Script Reset bei Simwechsel und Region Neustart ist nun wählbar um Simcrossing Fahrzeuge mit PMAC zu ermöglichen. (Bug in Opensim kompensieren)

Gender detection von AVA,NPC und Animationen (letzter Animationsbuchstaben F f oder M m) sowie Posen Zuordnung beim Hinsetzten

ES GIBT NOCH MEHR NEUE FUNKTIONEN, ABER DIE BESCHREIBEN ICH SPÄTER IN EINEM ANDEREN ARTIKEL

Ihr müsst es aber in 2 Teilen herunterladen und im Script Editor zu einem Script zusammen setzen.
Der Code ist zu lang für einen Artikel des Forums. Dieses ist NUR der 1. Teil!
Als eindeutige Beschriftung empfehle ich: P607 PMAC 2.8 Poser UNIVERSAL + NPC 122022.lsl

TEIL1:    "P607 PMAC 2.8 Poser UNIVERSAL + NPC 122022.lsl"

Code:
// P607 PARAMOUR MULTI-ANIMATION CONTROLLER (PMAC) v2.8-NPC Universal VERSION
// by Aine Caoimhe (Mata Hari)(c. LACM) March 2015-February 2016
// Provided under Creative Commons Attribution-Non-Commercial-ShareAlike 4.0 International license.
// Please be sure you read and adhere to the terms of this license: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// *** THIS SCRIPT REQUIRES (AND WILL ONLY WORK) IN REGIONS WHERE THE SCRIPT OWNER HAS OSSL FUNCTION PERMISSIONS ***
// *** THIS SCRIPT MUST ALWAYS BE LOCATED IN THE ROOT PRIM OF A LINKSET ***
// NPCs: NPC Rezzt nun im Gruppenkontext des Prims - damit NPCS auch auf Sims mit ausschließlich Gruppenzugang funktionieren.
// NPCs: Unterstützung für NPC Profil Foto. Fotos müssen ".bild npcname" heissen damit sie als Profilfoto im NPC Profil geladen werden.
// NPCs: Unterstützung für NPC Profil Info. Notecard muss ".info npcname" heissen, damit deren Inhalt im Info Feld des Profils angezeigt wird.
// Menü Rechte Änderung: NPC Tänzer können nun von jedem Nutzer geholt werden
// Admin Rechtestruktur erweitert vom Owner auf Berechtigte
// SIMCROSSING Fähigkeit: Script Reset bei Simwechsel und Region Neustart ist nun wählbar um Simcrossing Fahrzeuge mit PMAC zu ermöglichen. (Bug in Opensim kompensieren)
// Gender detection von AVA,NPC und Animationen (letzter Animationsbuchstaben F f oder M m) sowie Posen Zuordnung beim Hinsetzten
// *********************************************************
// *****  GENERAL USER SETTINGS - ADJUST AS PREFERED   *****
// *********************************************************
string  defaultGroup="Hexdance";    // name of a group (not the full card name!) to load by default regardless of its permission setting
integer resetOnQuit=FALSE;          // TRUE = when no more sitters, reset the script (will also load default group again)
                                    // FALSE = leave most recently loaded animation active
integer ownerUseReq=FALSE;          // TRUE = the owner must be the first to sit and other people can only sit while the owner is still present and seated
                                    // FALSE = no restriction...anyone can sit and use it at any time
integer ownerOnlyMenus=FALSE;       // TRUE = only the owner can access the dialog menus (can be turned off in options menu until script is reset or it is turned on again) - NOT RECOMMENDED unless ownerUseReq=TRUE
                                    // FALSE = anyone can be the controller
integer ownerUseUnlocksPerms=TRUE;  // TRUE = if owner is a current user, all users then have access to all groups and NPCs
                                    // FALSE = only the owner can ever load an owner-only Group or NPC
integer autoOn=TRUE;                // TRUE = will start in auto mode after a reset
                                    // FALSE = will start in manual mode after a reset -- after use will remain in whatever state it was left in unless resetOnQuit=TRUE
float   autoTimer=120.0;            // default time to use for the autotimer (in seconds) - after use will remain at whatever timer was last set to unless resetOnQuit=TRUE;
string  gs_ConfigName=".PMAC-CONFIG"; // Name of optional notecard with user defined configuration that overide the above values if present
integer showGroupsMenuFirst=FALSE;  // TRUE = when first initiating dialog, show the groups menu instead of the current group's animation menu; FALSE = show current group's animation menu
integer allowSoloNPC=TRUE;          // TRUE = NPCs can be left rezzed even if there are no avatars seated; FALSE = kill all NPCs if there are no remaining avatars
integer ownerOnlyRemote=TRUE;       // TRUE = remote commands will only be accepted from scripted objects belonging to the same owner as the PMAC system  (*** NEW IN PMAC 2.0***)
                                    // FALSE = anyone with authority to send osMessageObject() can send a command to PMAC
integer silent=TRUE;                // TRUE = PMAC will only say critical information to chat...usually only errors will be seen (*** NEW IN PMAC 2,0***)
                                    // FALSE = PMAC will notify users of some changes
integer resetOnSimcross= FALSE;     // TRUE/FALSE = restart script on Sim Restart and Simcrossing
// ***************************************
// *****  ADVANCED/BUILDER SETTINGS  *****
// ***************************************
string handleName="~~~positioner";    // inventory object to rez as a handle for positioning
list handleColours=[<1.000, 0.004, 0.667>,<0.004, 0.667, 1.000>,<0.667, 1.000, 0.004>,   // supply as many as you like but ideally at least as many as the expected positions in most of your menus
                    <1.000, 0.004, 0.004>,<0.004, 0.004, 1.000>,<1.000, 0.667, 0.004>,
                    <0.667, 0.004, 1.000>,<0.004, 1.000, 0.667>,<0.004, 1.000, 0.004>];
vector handleSize=<0.2,0.2,3.0>;    // size of handles used in edit mode
float handleAlpha=0.5;                // alpha of handles used in edit mode
string baseAn="~~~~~base_DO_NOT_DELETE_ME!!!!!";    // name of the P1 animation to use for synch
//
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// * * *  DO NOT CHANGE ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU'RE DOING! * * *
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
key user;
list positions;
list invNpc; // fullName | A/G/O | buttonName
integer invNpcStride=3;
list npcList;
integer npcPage;
list invGroups; // fullName | positions | A/G/O | buttonName
integer invGroupStride=4;
list groupList;
integer groupPage;
integer myChannel;
integer diaHandle;
string menu;
string txtDia;
list butDia;
string currentGroup;
list anData; // anName | command | A1Name | A1 Pos | A1 Rot |
integer anStride;
list anList;
list currentAn; // groupName | anName | command | A1Name | A1 Pos | A1 Rot |
integer anPage;
list editHandles;
integer rezzingHandles;
float editTimer=0.2;
string myState="INITIALIZING";
list specials; //buttonName | stringToSend
integer specPage;
integer gi_HaveConfig=0;
// NC_PROP ADDON
integer gi_NC_PROP_CHANGED=FALSE;   // global integer flag to indicate if new prop values have been sent form NC_PROPS addon
string gs_NC_PROP_DATA="";          // global string containing changed props data


key     toucher; // UUID des Operators
list    zugangsliste = ["b6a520ac-a468-4bc3-9e52-b509e88a8bce","18da7264-3260-4b63-8173-2a6fd7e93e33","798a6b1e-2186-4cf2-b733-460cb7132ae9"]; // UUIDS berechtigter AVAs
integer groupaccess = FALSE;  // TRUE/FALSE - darf die Gruppe steuern?
integer publicaccess = FALSE; // TRUE/FALSE - darf jeder steuern?
integer groupusercount;       // Anzahl der zulässigen User in die ser Posergruppe
list    anigenderlist = [];   // Speichert gender pro Animation

// *** ERMITTLUNG DES AVA/NPC GESCHLECHTS
integer GenderDetect(key id)
{
    string geschlecht =  osGetGender(id);
    if ( geschlecht == "female") return 1;
    else if  ( geschlecht == "male") return -1;
    else return 0;
}

// *** GESCHLECHTSSPEZIFISCHES POSITIONIEREN von AVAs und NPCs auf Animationen.
PositioniereAva( integer aktuellerplatz, key wer)
{
    integer usergeschlecht = GenderDetect(wer); // Geschlecht des aktuell zu platzierenden Users ermitteln
    integer anis = llGetListLength(anigenderlist);
    integer lauf = 0;
    integer angender = (integer) llList2String(anigenderlist,aktuellerplatz);  // Geschlecht der aktuelle Pose lesen
    if (usergeschlecht == angender) return; // ich sitze schon richtig   
    do
    {
       key nutzer = llList2String(positions,lauf);
       if(nutzer == NULL_KEY)
       {
            angender = (integer) llList2String(anigenderlist,lauf);
            if (angender == usergeschlecht) // Treffer, ich wechsel auf diese Position
            {
                SwapAvaPositions(aktuellerplatz,lauf);
                aktuellerplatz = lauf;
                return;
             }
       }
       lauf++;
    } while (lauf < anis);
}


// *** ZUGANGSBERECHTIGUNGEN AUSWERTEN
integer BerechtigungsCheck(string clicker, integer selbegruppe)
{
    integer zugang = FALSE;
    integer personalzugang = llGetListLength(zugangsliste);
    if (publicaccess)  { zugang = TRUE;}                    // Öffentlicher Zugang
    else if (selbegruppe && groupaccess) { zugang = TRUE;}  // Gruppen Zugang
    else if (clicker ==  llGetOwner()) { zugang = TRUE;}    // Besitzer Zugang immer erlaubt
    else if (personalzugang)                                // Persönlicher Zugang über UUID Liste
    {
        string personal;
        do
        {
            personal = llList2String(zugangsliste, personalzugang -1 );
            if( clicker ==  personal) { zugang = TRUE; }
            personalzugang--;
        }
        while ( personal && (!zugang));
    }
    return zugang;
}

// *** PMAC FENBEDIENUNG
handleRemoteCall(string message)
{
    list call=llParseString2List(message,["|"],[]);
    string command=llList2String(call,0);
    if (command=="PMAC_REMOTE_QUIT")
    {
        if (myState=="RUNNING") QuitEditMode();
        else if (command=="PMAC_REMOTE_SYNCH")
        {
            if (myState=="RUNNING") SyncAvas();
        }
        else
        {
            user=NULL_KEY;
            if (command=="PMAC_REMOTE_AUTO_OFF")
            {
                autoOn=FALSE;
                llSensorRemove();
            }
            else if (command=="PMAC_REMOTE_AUTO_ON")
            {
                autoOn=TRUE;
                float setTime=llList2Float(call,1);
                if (setTime>0.0) autoTimer=setTime;
                else updateAutoTimer(); // check anim to see if custom
                if (myState=="RUNNING") llSensorRepeat("THIS_WILL_NEVER_RETURN_A_SENSOR_RESULT",NULL_KEY,AGENT,0.001,0.0,autoTimer);
            }
            else if ((command=="PMAC_REMOTE_SWAP") && (myState=="RUNNING"))
            {
                integer p1=llList2Integer(call,1);
                integer p2=llList2Integer(call,2);
                if ((p1>=llGetListLength(positions))||(p2>=llGetListLength(positions))) llOwnerSay("ERROR: remote swap calling for a position index that is out of range. There are only "+(string)llGetListLength(positions)+" and you called for "+(string)p1+" and "+(string)p2+". Did you perhaps forget that indexing begins at position 0, not positions 1?");
                else SwapAvaPositions(p1,p2);
            }
            else if ((command=="PMAC_REMOTE_UNSIT") && (myState=="RUNNING"))
            {
                key keyToUnsit=llList2Key(positions,llList2Integer(call,1));
                if (keyToUnsit!=NULL_KEY) llUnSit(keyToUnsit);
            }
            else if ((command=="PMAC_REMOTE_SET_GROUP")||(command=="PMAC_REMOTE_SET_ANIMATION"))
            {
                string newGroup=llList2String(call,1);
                if (llListFindList(invGroups,[newGroup])==-1)
                {
                    llOwnerSay("ERROR! Remote call to load group \""+newGroup+"\" but this group doesn't exist. Please check the name and remember not to include the .menuxxxx prefix portion");
                    return;
                }
                loadAnimGroup(newGroup);
                if (command=="PMAC_REMOTE_SET_ANIMATION")
                {
                    string anToPlay=llList2String(call,2);
                    if (llListFindList(anList,[anToPlay])==-1)
                    {
                        llSay(0,"ERROR! A remote call was received to play animation \""+anToPlay+"\" from group \""+newGroup+"\" but that animation cannot be found. This is a fatal error and requires the system to quit and be reset. You may need to notify the owner and ask them to reset it if it isn't configured to do so automatically on quit.");
                        myState="ERROR";
                        QuitEditMode();
                    }
                    else playAnimation(anToPlay);
                }
            }
        }
    }
}

// *** ANIMATIONS AUSWAHL MENÜ ZEIGEN
showAnMenu()
{
    integer maxAn=llGetListLength(anList);
    integer showStart=(anPage+1);
    integer showEnd=(anPage+6);
    if (showEnd>maxAn) showEnd=maxAn;
    txtDia=""+"ANIMATION MENU: Select an animation\n"+currentlyPlaying()+"\nShowing animation";
    if (showEnd!=showStart) txtDia+="s "+(string)showStart+" to "+(string)showEnd;
    else txtDia+=" "+(string)showStart;
    txtDia+=" of "+(string)maxAn+ " total animations in this group\n\n"+llDumpList2String(llList2List(anList,anPage,anPage+5),"\n");
    butDia=[]+llList2List(anList,anPage,anPage+5);
    while (llGetListLength(butDia)<6) { butDia=[]+butDia+["-"]; }
    butDia=[]+butDia+["< PREV","ADD NPC","SYNCH","NEXT >","GROUPS","OPTIONS","QUIT"];
    menu="MENU_ANIM";
    startListening();
}

// *** ANIMATIONS GRUPPEN MENÜ ZEIGEN
showGroupsMenu()
{
    txtDia=""+"GROUPS MENU: Select a group of animations\n"+currentlyPlaying()+"\nShowing groups "+(string)(groupPage+1)+" to "+(string)(groupPage+6)+" of "+(string)llGetListLength(groupList)+ " total groups\n\n"+llDumpList2String(llList2List(groupList,groupPage,groupPage+5),"\n");
    butDia=[]+llList2List(groupList,groupPage,groupPage+5);
    while (llGetListLength(butDia)<6) { butDia=[]+butDia+["-"];}
    butDia=[]+butDia+["< PREV","SYNCH","NEXT >","<< BACK","OPTIONS","QUIT"];
    menu="MENU_GROUPS";
    startListening();
}

// *** EDIT MENÜ ZEIGEN
showEditMenu()
{
    txtDia=""+"EDIT MODE ACTIVE!!!\n"+currentlyPlaying()+" (#"+(string)(llListFindList(anList,[llList2String(currentAn,1)])+1)+" of "+(string)llGetListLength(anList)+")\n\nPlease ensure you have read and are familiar with the PMAC instructions for using the edit menu, particularly if you use add-on modules. You have been warned...";
    butDia=[]+["< PREV","SYNCH","NEXT >","REVERT THIS","STORE THIS","STORE ADDON","EDIT OFF","SAVE CARD","SAVE NEW"];
    menu="MENU_EDIT";
    startListening();
}

// *** OPTIONEN MENÜ ZEIGEN
showOptionsMenu()
{
    txtDia=""+"OPTIONS MENU:\n\n";
    butDia=[];
    if (user==llGetOwner())
    {
        if (ownerOnlyMenus)
        {
            txtDia+="EDIT ON enters edit mode (all positions must be filled)\nMENUS UNLOCK allows other users to take control\n";
            butDia=[]+butDia+["EDIT ON","MENUS UNLOCK","-"];
        }
        else
        {
            txtDia+="EDIT ON enters edit mode (all positions must be filled)\nMENUS LOCK prevents other users from taking control\n";
            butDia=[]+butDia+["EDIT ON","MENUS LOCK","-"];
        }
    }
    txtDia+="AUTO is used to enable, disable or adjust auto mode\n";
    butDia=[]+butDia+["AUTO","-"];
    if (llGetListLength(specials)>0)
    {
        txtDia+="SPECIAL access special add-on menus\n";
        butDia=[]+butDia+["SPECIAL"];
    }
    else butDia=[]+butDia+["-"];
    txtDia+="SWAP to swap positions\nUNSIT to force someone to stand up or remove a npc\n";
    butDia=[]+butDia+["SWAP","UNSIT"];
    if (llListFindList(positions,[NULL_KEY])>-1)
    {
        txtDia+="ADD NPC to have a NPC join you\n";   // ADD NPC Auswahl
        butDia=[]+butDia+["ADD NPC"];
    }
    else butDia=[]+butDia+["-"];
    butDia=[]+butDia+["<< BACK","SYNCH","QUIT"];
    menu="MENU_OPTIONS";
    startListening();
}

// *** SPECIALS MENÜ ZEIGEN
showSpecialsMenu()
{
    txtDia=""+"SPECIALS MENU\n\nThese are options supplied by any add-ons you have installed. Please consult their instructions for details.\n";
    butDia=[];
    integer i=specPage;
    while (llGetListLength(butDia)<9)
    {
        if (i<llGetListLength(specials))
        {
            butDia=[]+butDia+llList2String(specials,i);
            txtDia+="\n"+llList2String(specials,i);
            i+=2;
        } else butDia=[]+butDia+["-"];
    }
    butDia=[]+butDia+["< PREV","CANCEL","NEXT >"];
    menu="MENU_SPECIALS";
    startListening();
}

// *** AUTO MENÜ ZEIGEN
showAutoMenu()
{
    txtDia="AUTO MENU\n\nAuto mode is currently ";
    butDia=[]+["120","300","600","30","60","90"];
    if (autoOn)
    {
        txtDia+="ON and set to "+(string)llRound(autoTimer)+" seconds\n\nSelect a different time if you wish, or AUTO OFF to switch to manual mode";
        butDia=[]+butDia+["AUTO OFF","-","CANCEL"];
    }
    else
    {
        txtDia+="OFF\n\nSelect AUTO ON to use the last preset time or pick your preferred timer";
        butDia=[]+butDia+["AUTO ON","-","CANCEL"];
    }
    menu="MENU_SELECT_AUTO_MODE";
    startListening();
}

// *** ADD NPC MENÜ ZEIGEN
showAddNpcMenu()
{
    txtDia=""+"ADD NPC\n\nSelect the NPC to add. It will occupy the first available position\n\n"+llDumpList2String(llList2List(npcList,npcPage,npcPage+8),"\n");
    butDia=[]+llList2List(npcList,npcPage,npcPage+8);
    while (llGetListLength(butDia)<9) { butDia=[]+butDia+["-"]; }
    butDia=[]+butDia+["< PREV","CANCEL","NEXT >"];
    menu="MENU_ADD_NPC";
    startListening();
}

// *** GESPIELTE ANIMATION WECHSELN
playAnimation(string name)
{
    if (autoOn) llSensorRemove();
    integer i=llGetListLength(positions);
    while (--i>=0) { if (llList2Key(positions,i)!=NULL_KEY) osAvatarStopAnimation(llList2Key(positions,i),llList2String(currentAn,3+i*3));}
    integer indexAn=llListFindList(anData,[name]);
    list nextAn=[currentGroup]+llList2List(anData,indexAn,indexAn+anStride-1);
   
// Liste erstellen
    anigenderlist = [];
    integer nextanlaenge = llGetListLength(nextAn)/3;
    integer laufvar = 1;
    while (laufvar < nextanlaenge)
    {
        string animname = llList2String(nextAn,laufvar*3);
        integer laenge = llStringLength(animname);
        string buchstabe = llGetSubString(animname,laenge-1,-1);
        buchstabe = llToUpper(buchstabe);
        integer geschlecht = 0;
        if (buchstabe == "F") geschlecht = 1;
        else if (buchstabe == "M") geschlecht = -1;
        anigenderlist = anigenderlist + geschlecht;
        laufvar++;
    }
    integer have=llGetListLength(positions);
    integer need=llRound(((float)llGetListLength(nextAn)-3.0)/3.0);   
    while(have<need) { positions=[]+positions+[NULL_KEY];have++;}
    while ((have>need) && (llListFindList(positions,[NULL_KEY])>=0)) { positions=[]+llDeleteSubList(positions,llListFindList(positions,[NULL_KEY]),llListFindList(positions,[NULL_KEY]));have--;}
    if (have>need)
    {
        llSay(0,"There are currently too many users seated. You will need to reduce the number of seated users by "+(string)(have-need)+" to play it, or select a different group of animations");
        return;
    }
    i=llGetListLength(positions);
    while (--i>=0)
    {
        key who=llList2Key(positions,i);
        if (who!=NULL_KEY)
        {
            osAvatarPlayAnimation(who,llList2String(nextAn,3+i*3));
            setPosition(who,getUserLink(who),llList2Vector(nextAn,4+i*3),llList2Rot(nextAn,5+i*3));
            if (myState=="EDIT") setHandle(llList2Key(editHandles,i),llList2Vector(nextAn,4+i*3),llList2Rot(nextAn,5+i*3));
        }
    }
    currentAn=[]+nextAn;
    llMessageLinked(LINK_THIS,0,"GLOBAL_NEXT_AN|"+llList2String(currentAn,2),llDumpList2String(positions,"|"));
    if (autoOn)
    {
        updateAutoTimer();
        if (myState=="RUNNING") llSensorRepeat("THIS_WILL_NEVER_RETURN_A_SENSOR_RESULT",NULL_KEY,AGENT,0.001,0.0,autoTimer);
    }
}

updateAutoTimer()
{
    list comBlock=llParseString2List(llList2String(currentAn,2),["{","}"],[]);
    integer autoSet=llListFindList(comBlock,["PMAC_SET_AUTO"]);
    if (autoSet>=0)
    {
        if (llList2Float(comBlock,autoSet+1)>0.0) autoTimer=llList2Float(comBlock,autoSet+1);
        else llOwnerSay("ERROR: the animation \""+llList2String(currentAn,1)+"\" is calling for the auto timer to be set to "+llList2String(comBlock,autoSet+1)+". Value supplied must be a non-zero float. Using the previous timer value instead.");
    }
}

// *** AVATARPOSTITION MIT ANDEREN AVAS SYNCHRONISIEREN
SyncAvas()
{
    integer i=llGetListLength(positions);
    while (--i>=0)
    {
        key who=llList2Key(positions,i);
        if (who!=NULL_KEY) {
            osAvatarStopAnimation(who,llList2String(currentAn,3+i*3));
            osAvatarPlayAnimation(who,llList2String(currentAn,3+i*3));
        }
    }
    llMessageLinked(LINK_THIS,0,"GLOBAL_ANIMATION_SYNCH_CALLED",llDumpList2String(positions,"|"));
}

// *** AVATARPOSTITION UNTEREINANDER TAUSCHEN
SwapAvaPositions(integer indFrom,integer indTo)
{
    key whoFrom=llList2Key(positions,indFrom);
    if (whoFrom!=NULL_KEY)
    {
        osAvatarStopAnimation(whoFrom,llList2String(currentAn,3+indFrom*3));
        setPosition(whoFrom,getUserLink(whoFrom),llList2Vector(currentAn,4+indTo*3),llList2Rot(currentAn,5+indTo*3));
    }
    key whoTo=llList2Key(positions,indTo);
    if (whoTo!=NULL_KEY)
    {
        osAvatarStopAnimation(whoTo,llList2String(currentAn,3+indTo*3));
        setPosition(whoTo,getUserLink(whoTo),llList2Vector(currentAn,4+indFrom*3),llList2Rot(currentAn,5+indFrom*3));
    }
    positions=[]+llListReplaceList(positions,[whoFrom],indTo,indTo);
    positions=[]+llListReplaceList(positions,[whoTo],indFrom,indFrom);
    SyncAvas();
}

// *** MENÜKONTROLLE DURCH ANDEREN AVA ÜBERNEHMEN
ChangeMenuUser(key who)
{
    if ((user!=NULL_KEY)&&!silent) llRegionSayTo(user,0,llGetUsername(who)+" has now taken control of me");
    user=who;
    buildGroupList();
    specials=[];
    llMessageLinked(LINK_THIS,0,"GLOBAL_NEW_USER_ASSUMED_CONTROL|"+(string)user,llDumpList2String(positions,"|"));
    if (llListFindList(groupList,[currentGroup])==-1) loadAnimGroup(llList2String(groupList,0));
    else if (autoOn) showGroupsMenu();
    else if (showGroupsMenuFirst) showGroupsMenu();
    else showAnMenu();
}

// *** EDITMODUS VERLASSEN
QuitEditMode()
{
    if (myState=="EDIT") removeHandles();
    integer i=llGetListLength(positions);
    while (--i>=0)
    {
        key who=llList2Key(positions,i);
        if (who!=NULL_KEY)
        {
            if (osIsNpc(who)) osNpcRemove(who);
            else
            {
                if (!silent) llRegionSayTo(who,0,"Quit called");
                llUnSit(who);
            }
        }
    }
}

// *** NEUE ANIMATIONSGRUPPE LADEN
loadAnimGroup(string name)
{
    if (name==currentGroup) { if(user!=NULL_KEY) showAnMenu(); return;}
    integer indToLoad=llListFindList(invGroups,[name])-3;
    groupusercount=llList2Integer(invGroups,indToLoad+1);  // Zugelassene Userzahl in der Animations Gruppe
    if (groupusercount<llGetListLength(positions))  // Gruppe enthält zu wenige Animationen für sitzende User
    {
        integer agents;
        integer a=llGetListLength(positions);
        while (--a>-1) { if (llList2Key(positions,a)!=NULL_KEY) agents++;}
        if (agents>groupusercount)
        {
            llRegionSayTo(user,0,"You cannot use animations from the "+name+" group at the moment because it is for a maximum of "+(string)groupusercount+" users and there are currently "+(string)agents+" seated. Please select a different group of animations, remove NPCs, or ask someone to stand.");
            if (user!=NULL_KEY) startListening();
            return;
        }
    }
    currentGroup=name;      // Erfolgreich auf neue Animations Gruppe geschalten
    anData=[]+llParseString2List(osGetNotecard(llList2String(invGroups,indToLoad)),["|","\n"],[""]);
    anStride=2+(3*groupusercount);
    anPage=0;
    anList=llList2ListStrided(anData,0,-1,anStride); // Animationsliste füllen
    if (autoOn && (myState=="RUNNING"))
    {
        playAnimation(llList2String(anList,0));
        if (user!=NULL_KEY)
        {
            if (!silent) llRegionSayTo(user,0,"Now automatically playing animations from "+name);
            showGroupsMenu();
        }
    } else if (user!=NULL_KEY) showAnMenu();
}

// *** PMAC KONFIGURATIONSPARAMETER LADEN
loadConfig()
{
    integer i;
    float n;
    string  sParam;
    string  sValue;
    list lParams=[];
    for(i=0;i<osGetNumberOfNotecardLines(gs_ConfigName); i++)
    {
        lParams=llParseString2List(osGetNotecardLine(gs_ConfigName,i),["="],"");
        if (llGetListLength(lParams)==2)
        {
            sParam=llStringTrim(llList2String(lParams,0),STRING_TRIM);
            sValue=llStringTrim(llList2String(lParams,1),STRING_TRIM);
            if ((sParam=="defaultGroup") || (sParam=="DefaultGroup")) { defaultGroup=sValue; }
            else if ((sParam=="resetOnQuit")||(sParam=="ResetOnQuit")) { resetOnQuit=(sValue=="TRUE");}
            else if ((sParam=="ownerUseReq")||(sParam=="OwnerUseReq")) { ownerUseReq=(sValue=="TRUE");}
            else if ((sParam=="ownerOnlyMenus")||(sParam=="OwnerOnlyMenus")) { ownerOnlyMenus=(sValue=="TRUE");}
            else if ((sParam=="ownerUseUnlocksPerms")||(sParam=="OwnerUseUnlockPerms")) { ownerUseUnlocksPerms=(sValue=="TRUE");}
            else if ((sParam=="autoOn")||(sParam=="AutoOn")) { autoOn=(sValue=="TRUE");}
            else if ((sParam=="autoTimer")||(sParam=="AutoTimerValue")) { autoTimer=(float)sValue;}
            else if ((sParam=="baseAn")||(sParam=="BaseAnimation"))    { baseAn=sValue;}
            else if ((sParam=="showGroupsMenuFirst")||(sParam=="ShowGroupsMenuFirst")) { showGroupsMenuFirst=(sValue=="TRUE");}
            else if ((sParam=="allowSoloNPC")||(sParam=="AllowSoloNPC")) { allowSoloNPC=(sValue=="TRUE");}
            else if ((sParam=="ownerOnlyRemote")||(sParam=="OwnerOnlyRemote")) { ownerOnlyRemote=(sValue=="TRUE");}
            else if ((sParam=="silent")||(sParam=="Silent")) { silent=(sValue=="TRUE");}
            else { llOwnerSay("Warn: Unable to parse user defined configuration "+llDumpList2String(lParams,"="));}
        }
        else if (llGetListLength(lParams)!=0) { llOwnerSay("Warn: Skipping user defined configuration line "+(string)i+"  "+llDumpList2String(lParams,"="));}
    }
}

// *** ANIMTIONS GRUPPEN LISTE ERSTELLEN
buildGroupList()
{
    groupList=[];
    groupPage=0;
    integer i;
    while (i<llGetListLength(invGroups))
    {
        if ( ( llList2String(invGroups,i+2)=="A" ) || ( (llList2String(invGroups,i+2)=="G") && llSameGroup(user)) || (user==llGetOwner()) ||  (llListFindList(positions,[llGetOwner()])>=0)  ) groupList=[]+groupList+[llList2String(invGroups,i+3)];
        i+=invGroupStride;
    }
}

// *** LISTE VERFÜGBARER NPCS ERSTELLEN für Menüauswahl
buildNpcList()
{
    npcList=[];
    npcPage=0;
    integer i;
    while (i<llGetListLength(invNpc))
    {// NPCs darf nun jeder laden
        npcList=[]+npcList+[llList2String(invNpc,i+2)];
        i+=invNpcStride;
    }
    showAddNpcMenu();
}

// *** INVENTAR AUSWERTEN
buildInventoryLists()
{
    invGroups=[];
    invNpc=[];
    integer i=llGetInventoryNumber(INVENTORY_NOTECARD);
    while (--i>-1)
    {
        string name=llGetInventoryName(INVENTORY_NOTECARD,i);
        if (llSubStringIndex(name,".menu")==0)
        {
            integer nameStarts=llSubStringIndex(name," ")+1;
            if (llListFindList(invGroups,[llGetSubString(name,nameStarts,-1)])>-1) llOwnerSay("ERROR! You have two groups notecards where the name \""+llGetSubString(name,nameStarts,-1)+"\" is used for the button! Group button names must be unique");
            else invGroups=[]+[name,(integer)(llGetSubString(name,7,nameStarts-3)),llGetSubString(name,nameStarts-2,nameStarts-2),llGetSubString(name,nameStarts,-1)]+invGroups;
        }
        else if (llSubStringIndex(name,".NPC")==0)
        {
            if (llListFindList(invNpc,[llGetSubString(name,8,-1)])>-1) llOwnerSay("ERROR! You have two NPC notecards where the NPC name is \""+llGetSubString(name,8,-1)+"\" but NPC names must be unique");
            else invNpc=[]+[name,llGetSubString(name,6,6),llGetSubString(name,8,-1)]+invNpc;
        }
        else if (llSubStringIndex(name,gs_ConfigName)==0) { gi_HaveConfig=1;}
    }
}

// *** ANIMATIONS GRUPPEN NOTECARD SPEICHERN
saveCard(string cardName)
{
    if (llGetInventoryType(cardName)==INVENTORY_NOTECARD)
    {
        llRemoveInventory(cardName);
        llSleep(0.25);
    }
    integer i;
    integer l=llGetListLength(anData);
    string dataToStore;
    while (i<l)
    {
        dataToStore+=llDumpList2String(llList2List(anData,i,i+anStride-1),"|")+"\n";
        i+=anStride;
    }
    osMakeNotecard(cardName,dataToStore);
    llMessageLinked(LINK_THIS,0,"GLOBAL_EDIT_STORE_TO_CARD|"+cardName,llDumpList2String(positions,"|"));
}

// *** DIE LINKNUMMER DES AVATARS HERAUSFINDEN UND ZURÜCKMELDEN
integer getUserLink(key who)
{
    integer ret=FALSE;
    if (who!=NULL_KEY)
    {
        integer link=llGetNumberOfPrims();
        while ((link>1) && (ret==FALSE))
        {
            key this=llGetLinkKey(link);
            if(this==who) ret=link;
            link--;
        }
    }
    return ret;
}

// *** MENÜ DIALOG MIT USER INITIALISIEREN
startListening()
{ llDialog(user,txtDia,llList2List(butDia,9,11)+llList2List(butDia,6,8)+llList2List(butDia,3,5)+llList2List(butDia,0,2),myChannel);}

// *** RÜCKMELDUNG AUTOMODE TIMER EINSTELLUNG UND AKTUELLER ANIMATION
string currentlyPlaying()
{
    string strToReturn="Currently playing: "+llList2String(currentAn,0)+" > "+llList2String(currentAn,1);
    if (autoOn) strToReturn+="\nAUTO mode is on and set to "+(string)llRound(autoTimer)+" seconds";
    return strToReturn;
}

// *** NACH AVA POSITIONSEINSTELLUNG DIE NEUEN ORTSWERTE ZURÜCKSCHREIBEN
persistChanges()
{
    integer u=llGetListLength(positions);
    while(--u>=0)
    {
        list avData=getPosition(llList2Key(positions,u),getUserLink(llList2Key(positions,u)));
        vector pos=llList2Vector(avData,0);
        rotation rot=llList2Rot(avData,1);
        string strPos="<"+trimF(pos.x)+","+trimF(pos.y)+","+trimF(pos.z)+">";
        string strRot="<"+trimF(rot.x)+","+trimF(rot.y)+","+trimF(rot.z)+","+trimF(rot.s)+">";
        currentAn=[]+llListReplaceList(currentAn,[strPos,strRot],4+u*3,5+u*3);
    }
    integer anIndex=llListFindList(anData,llList2List(currentAn,1,1));
    if(gi_NC_PROP_CHANGED)
    {    // NC_PROPS
    string old_cmd=llList2String(currentAn,2);
        integer NC_PROP_data_start=llSubStringIndex(old_cmd,"NC_PROP{")+8; // find start
        // test is little weird, as i added +8 to compensate for search string length
        if (NC_PROP_data_start != 7)
        {    // search ending delimiter in rest of string
            integer NC_PROP_data_end=NC_PROP_data_start+llSubStringIndex(llGetSubString(old_cmd, NC_PROP_data_start,-1),"}");
            string new_cmd=llGetSubString(old_cmd, 0, NC_PROP_data_start -1) + gs_NC_PROP_DATA + llGetSubString(old_cmd, NC_PROP_data_end,-1);
            currentAn=[]+llListReplaceList(currentAn,[new_cmd],2,2);
        }
        gi_NC_PROP_CHANGED=FALSE;
    }
    anData=[]+llListReplaceList(anData,llList2List(currentAn,1,-1),anIndex,anIndex+anStride-1);
    llMessageLinked(LINK_THIS,0,"GLOBAL_EDIT_PERSIST_CHANGES",llDumpList2String(positions,"|"));
}

// *** HANDLES ZUR AVA POSITIONEN EDIT REZZEN
rezHandles()
{
    if (llGetListLength(editHandles)<llGetListLength(positions)) llRezObject(handleName,llGetPos(),ZERO_VECTOR,ZERO_ROTATION,0);
    else
    {
        rezzingHandles=FALSE;
        integer h=llGetListLength(editHandles);
        while (--h>=0) { osSetPrimitiveParams( llList2Key(editHandles,h),[PRIM_SIZE,handleSize,PRIM_COLOR,ALL_SIDES,llList2Vector(handleColours,h%llGetListLength(handleColours)),handleAlpha,PRIM_TEXT,"pos "+(string)(h+1),llList2Vector(handleColours,h%llGetListLength(handleColours)),1.0,PRIM_NAME,"pos "+(string)(h+1)]); }
        playAnimation(llList2String(currentAn,1));
        llSetTimerEvent(editTimer);
        SyncAvas();
        showEditMenu();
    }
}

// *** HANDLES ZUR AVA POSITIONEN EDIT ENTFERNEN
removeHandles()
{
    llSetTimerEvent(0.0);
    myState="RUNNING";
    llMessageLinked(LINK_THIS,0,"GLOBAL_NOTICE_LEAVING_EDIT_MODE",llDumpList2String(positions,"|"));
    integer l=llGetListLength(editHandles);
    while (--l>=0) { osMessageObject(llList2Key(editHandles,l),"HANDLE_DIE");}
    editHandles=[];
    showOptionsMenu();
}

// *** HANDLES ZUR AVA POSITIONEN EDIT VERSCHIEBEN/POSITIONIEREN
setHandle(key prim, vector relPos, rotation relRot)
{
    vector pos=relPos*llGetRot()+llGetPos();
    rotation rot=relRot*llGetRot();
    osSetPrimitiveParams(prim,[PRIM_POSITION,pos,PRIM_ROTATION,rot]);
}

// *** AVATAR AN ANDERE POSITION FÜR NEUE ANIMATION BEFÖRDERN
setPosition(key who, integer link, vector pos, rotation rot)
{
    vector size = llGetAgentSize(who);
    float fAdjust = ((((0.008906 * size.z) + -0.049831) * size.z) + 0.088967) * size.z;
    llSetLinkPrimitiveParamsFast(link,[PRIM_POS_LOCAL, ((pos + <0.0, 0.0, 0.4>) - (llRot2Up(rot) * fAdjust)), PRIM_ROT_LOCAL, rot]);
}

// *** AKTUELLE AVATARPOSITION ERMITTELN
list getPosition(key who, integer link)
{
    vector size = llGetAgentSize(who);
    float fAdjust = ((((0.008906 * size.z) + -0.049831) * size.z) + 0.088967) * size.z;
    list avData=llGetLinkPrimitiveParams(link,[PRIM_POS_LOCAL,PRIM_ROT_LOCAL]);
    vector avPos=llList2Vector(avData,0);
    rotation avRot=llList2Rot(avData,1);
    vector avPosUnadjusted=(avPos - <0.0, 0.0, 0.4>) + (llRot2Up(avRot) * fAdjust);
    return [avPosUnadjusted,avRot];
}

// *** REGIONS POSITION IN RELATIVE POSITION UMRECHNEN
list regToRel(vector regionPos,rotation regionRot)
{
    vector relPos=(regionPos - llGetPos()) / llGetRot();
    rotation relRot=regionRot/ llGetRot();
    return [relPos,relRot];
}

// *** RELATIVE POSITION IN REGIONS POSITION UMRECHNEN
list relToReg(vector refPos,rotation refRot)
{
    vector regionPos=refPos*llGetRot()+llGetPos();
    rotation regionRot=refRot*llGetRot();
    return [regionPos,regionRot];
}

// *** HILFSFUNKTION ZUR POSITIONS BERECHNUNG
string trimF(float value)
{
    integer newVal=llRound(value*10000);
    integer negFlag=FALSE;
    if (newVal<0) { negFlag=TRUE; newVal*=-1; }
    integer strLength;
    string retStr;
    if (newVal==0) retStr="0";
    else if (newVal<10) retStr="0.000"+(string)newVal;
    else if (newVal<100) retStr="0.00"+(string)newVal;
    else if (newVal<1000) retStr="0.0"+(string)newVal;
    else if (newVal<10000) retStr="0."+(string)newVal;
    else
    {
        retStr=(string)newVal;
        strLength=llStringLength(retStr);
        retStr=llGetSubString(retStr,0,strLength-5)+"."+llGetSubString(retStr,strLength-4,strLength-1);
    }
    while (llGetSubString(retStr,strLength,strLength)=="0")
    {
        retStr=llGetSubString(retStr,0,strLength-1);
        strLength-=1;
    }
    if (negFlag) retStr="-"+retStr;
    return retStr;
}

// NPC im GRUPPENKONTEXT rezzen, auch für public gesperrte Sims. Mit NPC Userprofilinfo und Profilbild
key npc_rezzer(string npcnotecard, key sittarget, vector npcPos)
{
    string npc_name = llGetSubString(npcnotecard,8,-1); // NPC Namen ermitteln
    key npctorez = osNpcCreate(npc_name, "" , npcPos, npcnotecard, 8 | OS_NPC_NOT_OWNED | OS_NPC_SENSE_AS_AGENT | OS_NPC_OBJECT_GROUP );
    osNpcSit(npctorez,sittarget,OS_NPC_SIT_NOW);
    if (llGetInventoryKey(".bild " + npc_name)) osNpcSetProfileImage(npctorez,".bild "+ npc_name);  // Testen ob Profilbild vorhanden - dann laden
    if (llGetInventoryKey(".info " + npc_name) != (string)NULL_KEY )
    {
        string info = (string) osGetNotecard(".info " + npc_name);
        osNpcSetProfileAbout(npctorez, info);  // Testen ob Profilinfo Notecard vorhanden - dann laden
    }
    return npctorez;
}

// *** HAUPTMENÜ
default
{
    // *** INITIALISIERUNG UND GRUNDLIEGENDE ÜBERPRÜFUNG
    state_entry()
    {
        if (llGetAttached()) return;
        if (llGetLinkNumber()>1)
        {
            myState="ERROR";
            llOwnerSay("ERROR! The main PMAC controller script must always be located in the root prim of a linkset!");
            return;
        }
        myChannel=0x80000000|(integer)("0x"+(string)llGetKey());
        user=NULL_KEY;
        buildInventoryLists();
        if (gi_HaveConfig) loadConfig();
        if (llGetInventoryType(baseAn)!=INVENTORY_ANIMATION)
        {
            llOwnerSay("ERROR! Unable to find the base priority 1 animation to use for synch: "+baseAn);
            myState="ERROR";
            return;
        }
        if (llListFindList(invGroups,[defaultGroup])==-1)
        {
            myState="ERROR";
            llOwnerSay("ERROR! Unable to find the specified default group \""+defaultGroup+"\" in inventory. Make sure you supplied the simple group name, not the full card name");
            return;
        }
        loadAnimGroup(defaultGroup);
        integer i=llList2Integer(invGroups,llListFindList(invGroups,[defaultGroup])-2);
        positions=[];
        while (--i>=0) { positions=[]+positions+[NULL_KEY]; }
        currentAn=[]+[currentGroup]+llList2List(anData,0,anStride-1);
        myState="READY";
        llMessageLinked(LINK_THIS,0,"GLOBAL_SYSTEM_RESET",NULL_KEY);
        llSitTarget(<0.0,0,0.001>,ZERO_ROTATION);
        if (!silent) llOwnerSay("Initialization complete and ready to use");
    }

    // *** INITIALER RESET BEIM REZZEN DES POSERS
    on_rez(integer num) {llResetScript();}

    // *** HILFSOBJEKTE FÜR EDITMODUS REZZEN
    object_rez(key id)
    {
        if (!rezzingHandles) return;
        editHandles=[]+editHandles+[id];
        rezHandles();
    }

    // ***
    sensor(integer num)
    {
        llOwnerSay("ERROR! Sensor event inexplicably returned a result!");
        llSensorRemove();
        autoOn=FALSE;
    }

    // ***
    no_sensor()
    {
        if (!autoOn)
        {
            llOwnerSay("ERROR! Sensor repeat triggered but auto mode is off. Figure out how this can happen and fix. Sensor removed");
            llSensorRemove();
            return;
        }
        if (myState!="RUNNING")
        {
            llOwnerSay("ERROR! Sensor repeat triggered while not in normal running state: Please figure out how this happened and fix. Sensor removed and auto turned off.\nState was=: "+myState);
            autoOn=FALSE;
            llSensorRemove();
            return;
        }
        integer i=llListFindList(anList,[llList2String(currentAn,1)]);
        if (i==-1) i=0;
        else
        {
            i++;
            if (i>=llGetListLength(anList)) i=0;
        }
        playAnimation(llList2String(anList,i));
    }

    // *** PERIODISCHE PRÜFUNG EDIT MODUS ; ansonsten TIMER löschen
    timer()
    {
        if(myState=="EDIT")
        {
            llSetTimerEvent(0.0);
            integer l=llGetListLength(positions);
            while (--l>-1)
            {
                key who=llList2Key(positions,l);
                if (who==NULL_KEY)
                {
                    llOwnerSay("ERROR! NULL_KEY user while processing timer event edit mode. Leaving edit mode without saving changes.");
                    removeHandles();
                    return;
                }
                if (llGetAgentSize(who)==ZERO_VECTOR)
                {
                    llOwnerSay("ERROR! Cannot detect user in region while processing timer event edit mode. Leaving edit mode without saving changes");
                    removeHandles();
                    return;
                }
                list handleData=llGetObjectDetails(llList2Key(editHandles,l),[OBJECT_POS,OBJECT_ROT]);
                if (llGetListLength(handleData)==0)
                {
                    llOwnerSay("ERROR! Unable to detect a handle! Leaving edit mode without saving changes");
                    removeHandles();
                    return;
                }
                handleData=[]+regToRel(llList2Vector(handleData,0),llList2Rot(handleData,1));
                setPosition(who,getUserLink(who),llList2Vector(handleData,0),llList2Rot(handleData,1));
            }
            llSetTimerEvent(editTimer);
        } else llSetTimerEvent(0.0);
    }

    // *** ABHANDLUNG DES PMAC REMOTEKOMMANDOS
    dataserver(key qid, string message)
    {
        if ((ownerOnlyRemote) && (llGetOwnerKey(qid)!=llGetOwner())) return;
        if (llSubStringIndex(message,"PMAC_REMOTE_")==0) handleRemoteCall(message);
    }

    // *** NACHRICHTEN VON LINKOBJEKTEN AUSWERTEN
    link_message(integer sender,integer flag,string message,key command)
    {
        if (flag!=-1) return;
        if (message=="MAIN_RESUME_MAIN_DIALOG") showOptionsMenu();
        else if (llSubStringIndex(message,"MAIN_REGISTER_MENU_BUTTON")==0)
        {
            string buttonName=llList2String(llParseString2List(message,["|"],[]),1);
            integer locationToAdd=llListFindList(specials,[buttonName]);
            if (locationToAdd==-1) specials=[]+specials+[buttonName,command];
            else specials=[]+llListReplaceList(specials,[buttonName,command],locationToAdd,locationToAdd+1);
            specials=[]+llListSort(specials,2,TRUE);
        }
        else if (llSubStringIndex(message,"MAIN_UNREGISTER_MENU_BUTTON")==0)
        {
            string buttonName=llList2String(llParseString2List(message,["|"],[]),1);
            integer locationToKill=llListFindList(specials,[buttonName,command]);
            if (locationToKill==-1) return;
            else specials=[]+llDeleteSubList(specials,locationToKill,locationToKill+1);
            specials=[]+llListSort(specials,2,TRUE);
        }
        else if (llSubStringIndex(message,"NC_PROP_UPDATE")==0)
        { // NC_PROP addon
            gi_NC_PROP_CHANGED=TRUE;
            gs_NC_PROP_DATA=llGetSubString(message,15,-1);
        }
        else if (llSubStringIndex(message,"PMAC_REMOTE_")==0) handleRemoteCall(message);
    }

    // *** ANZAHL SITZENDER USER HAT SICH GEÄNDERT; ODER BESITZERWECHSEL DES POSERS
    changed (integer change)
    {
        if (change & CHANGED_LINK)
        {
            if (llGetLinkNumber()>1)
            {
                llOwnerSay("ERROR! You have changed the linkset and the PMAC main script is no longer located in the root prim!");
                myState="ERROR";
                return;
            }
            integer i=llGetNumberOfPrims();
            integer l=llGetObjectPrimCount(llGetKey());
            list seated;
            integer realAvi;
            while (i>l)
            {
                key who=llGetLinkKey(i);
                if ((myState=="ERROR") || (myState=="INITIALIZING"))
                {
                    if (osIsNpc(who)) osNpcRemove(who);
                    else
                    {
                        if (myState=="ERROR") llRegionSayTo(who,0,"Sorry, I have encountered an error and must shut down until it is corrected");
                        else llRegionSayTo(who,0,"Sorry, you cannot sit while I am initializing. Please wait a moment and try again.");
                        llUnSit(who);
                    }
                }
                else
                {
                    seated=[]+[who]+seated;
                    if (!osIsNpc(who)) realAvi++;
                    if (llListFindList(positions,[who])==-1)
                    { // new sitter
                        integer indexToSit=llListFindList(positions,[NULL_KEY]);                
                        if (ownerUseReq && (who!=llGetOwner()) && (llListFindList(positions,[llGetOwner()])==-1))
                        {
                            llRegionSayTo(who,0,"Sorry, the system is set to require that the owner is using me before anyone else may sit.");
                            llUnSit(who);
                        }
                        else if (indexToSit==-1)
                            {
                                llRegionSayTo(who,0,"Sorry, there are no available positions for you to occupy. Please wait for someone to stand");
                                llUnSit(who);
                            }
                        else
                        {
                            positions=[]+llListReplaceList(positions,[who],indexToSit,indexToSit);
                            llSleep(0.2);
                            list anToStop=llGetAnimationList(who);
                            osAvatarPlayAnimation(who,baseAn);
                            integer stop=llGetListLength(anToStop);
                            key dontStop=llGetInventoryKey(baseAn);
                            while (--stop>-1) { osAvatarStopAnimation(who,llList2Key(anToStop,stop)); }
                            if (myState=="READY")
                            {
                                if ((osIsNpc(who)) && (!allowSoloNPC))
                                {
                                    llSay(0,"Sorry, an NPC cannot sit until there is a human user in control. Unsitting your NPC.");
                                    llUnSit(who);
                                    positions=[]+llListReplaceList(positions,[NULL_KEY],indexToSit,indexToSit);
                                    osAvatarStopAnimation(who,baseAn);
                                }
                                else
                                {
                                    myState="RUNNING";
                                    llMessageLinked(LINK_THIS,0,"GLOBAL_START_USING",llDumpList2String(positions,"|"));
                                    playAnimation(llList2String(currentAn,1));
                                }
                            }
                            else { playAnimation(llList2String(currentAn,1));}
                            PositioniereAva(indexToSit, who);  // Geschlechtsspezifische Positionierung
                        }
                    }
                }
                i--;
            }
            i=llGetListLength(positions);
            while (--i>=0)
            {
                key who=llList2Key(positions,i);
                if (who!=NULL_KEY)
                {
                    if ((!realAvi)&&osIsNpc(who))
                    {
                        if (!allowSoloNPC)
                        {
                            osNpcRemove(who);
                            positions=[]+llListReplaceList(positions,[NULL_KEY],i,i);
                        }
                    }
                    else if (llListFindList(seated,[who])==-1)
                    {
                        if (myState=="EDIT")
                        {
                            llOwnerSay("WARNING! Someone stood! Leaving edit mode and no changes will be stored to card");
                            removeHandles();
                            if (who!=llGetOwner())showOptionsMenu();
                        }
                        if (llGetAgentSize(who)!=ZERO_VECTOR)
                        {
                            osAvatarPlayAnimation(who,"stand");
                            osAvatarStopAnimation(who,llList2String(currentAn,3+i*3));
                            osAvatarStopAnimation(who,baseAn);
                            if (who==user) user=NULL_KEY;
                        }
                        positions=[]+llListReplaceList(positions,[NULL_KEY],i,i);
                        llMessageLinked(LINK_THIS,0,"GLOBAL_USER_STOOD|"+(string)i+"|"+(string)who,llDumpList2String(positions,"|"));
                    }
                }
            }
            if (!realAvi)
            {
                if (diaHandle)
                {
                    llListenRemove(diaHandle);
                    diaHandle=FALSE;
                }
                if (llGetListLength(seated)<1)
                {
                    if (autoOn) llSensorRemove();
                    myState="READY";
                    llMessageLinked(LINK_THIS,0,"GLOBAL_SYSTEM_GOING_DORMANT",NULL_KEY);
                    if (resetOnQuit) llResetScript();
                }
            }
        }
        else if (change & CHANGED_REGION_START) {if(resetOnSimcross) llResetScript();}
        else if (change & CHANGED_OWNER) llResetScript();
    }
   
    // *** MENÜ DURCH BERÜHREN DES POSERS ANGEFORDERT
    touch_start(integer num)
    {
        toucher=llDetectedKey(0);
        integer gruppe = llDetectedGroup(0);
        integer access = BerechtigungsCheck(toucher,gruppe);

        if (osIsNpc(llDetectedKey(0))) return;
        if (myState=="ERROR")
        {
            if (toucher==llGetOwner()) llOwnerSay("PMAC somehow entered ERROR state. Please check your chat log for one or more messages indicating the nature of the error, correct it, then reset the script");
            else llRegionSayTo(toucher,0,"Sorry, I encountered an error and am waiting for the owner to correct the issue and restart me");
        }
        else if (myState=="INITIALIZING") llRegionSayTo(toucher,0,"Please wait until initialization is complete, then try again");
        // else if (myState=="READY") llRegionSayTo(toucher,0,"Please sit to begin using me");
        else if (myState=="EDIT")
        {
            if (toucher!=llGetOwner()) llRegionSayTo(toucher,0,"Sorry, the owner is currently editting positions and must remain in control of the dialog until this is complete.");
            else startListening();
        }
        else if (myState=="RUNNING")
        {
            if ((!access) && (llListFindList(positions,[toucher])==-1)) llRegionSayTo(toucher,0,"Only current users may access the controls. Please sit, then try again");
            else if ((!access) &&(ownerOnlyMenus && (toucher!=llGetOwner()))) { if (!silent) llRegionSayTo(toucher,0,"Sorry, this item is currently set to only allow the owner to access the controls.");}
            else if (toucher!=user)
            {
                if (user==NULL_KEY)
                {
                    if (!diaHandle) diaHandle=llListen(myChannel,"",NULL_KEY,"");
                    ChangeMenuUser(toucher);
                }
                else llDialog(toucher,"Please confirm that you want to take control of me",["TAKE CONTROL","CANCEL"],myChannel);
            }
            else startListening();
        }
    }


TEIL2: "P607 PMAC 2.8 Poser UNIVERSAL + NPC 122022.lsl" befindet sich im nächsten Artikel!!!!!!!
Zitieren


Gehe zu:


Benutzer, die gerade dieses Thema anschauen: 1 Gast/Gäste