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.

Paramour Line Dancer Script für Opensim in OSSL Regionen mit NPC Unterstützung
#1
Paramour Line Dancer Script für Opensim in OSSL Regionen mit NPC Unterstützung

Folgendes Script basiert auf der Arbeit von Mata Hari / Aine Caoimhe.

Und ist unter folgenden Lizenzbedingungen einzusetzten:           https://creativecommons.org/licenses/by-nc-sa/4.0/

Hier nun die 2. Versprochene Variante für OPENSIM Regionen die OSSL Befehle freigeschalten haben.
Dies ist die Basis damit auch NPCs funktionieren.

ACHTUNG: DIESE VERSION funktioniert nur wenn entsprechende OSSL Befehle für die Region freigegeben sind, und der Primbesitzer ausreichend Rechte hat diese zu nutzen.

Es gilt alles was ich schon im Artikel  "Paramour Line Dancer Script für SL und Opensim in nicht OSSL Regionen" gesagt habe.
Das werde ich an dieser Stelle auch nicht wiederholen.
Hier der Link zum Nachlesen:  https://service-silberwelten.de/showthread.php?tid=157

So- was ist hier anders? Und warum sollte man diese Version einsetzten wo es doch die Andere gibt, die in allen Regionen läuft?
Die Antwort sind die NPCs.
Die gab es auch schon im Original von Mata Hari / Aine Caoimhe.
Aber genau an diesem Punkt haben sich die meisten Verbesserungen in Opensim abgespielt.

Im Detail ist hier folgendes nun im Script verändert:
NPCs können auch auf gesperrten Sims gerezzt werden, wenn in dem Prim aus dem sie gestartet werden die gültige Landgruppe der Sim eingestellt ist.
Ebenso überleben sie dann auch ein nachträgliches Sperren der Sim. Die Gruppe muss aber erlaubt bleiben.

Ausserdem unterstützt dieses Script nun auch für NPCs Profilfotos und Profilinfos.
Ein Dancline NPC hat also nun auch ein Profilbild und eine Beschreibung sofern ihr die anlegt.

Dafür habe ich eine feste Notation eingeführt. Diese ermöglicht es mehrere NPCs unabhängig bereitzustellen.
Ausserdem ziehe ich die gleich Notation auch in allen anderen NPC Scripten nach, damit die Karten einfach austauschbar sind.
Bei dem NPC Beschreibungs Notecards habe ich die Nomenklatur von PMAC zugrunde gelegt.

Eine vollständige NPC Beschreibung besteht also künftig aus 3 Notecards.
Der erste Teil der Notecard ist die Kennung was für eine Karte das ist. Der 2.Teil jeder Notecard ist ein identischer NPC Name.

NPC Outfit Beschreibung:    .NPC123NPCNAME           PFLICHTEINTRAG
NPC Foto:                               .bildNPCNAME                  Optionaler Eintrag
NPC Beschreibung:               .infoNPCNAME                  Optionaler Eintrag

Beispiel:     .NPC123Susan Vogue    
                  .bildSusan Vogue
                  .infoSusan Vogue

Gross/Klein Schreibung und eventuelle Leerzeichen hinter den NPC Namen sind zu beachten.

Für die NPC Outfit Beschreibung gibt PMAC folgendes vor:
.NPC ist die Kennung das es sich um eine Outfit Karte handelt.
Danach folgt eine beliebige 3-stellige Zahl, die von PMAC aber zur Sortierung genutzt wird.
Anschliessend folgt der NPCName. Dieser darf auch 2-teilig mit Leerzeichen dazwischen sein.

Ausserdem neu ist das Berechtigungs System wer NPCs rezzen und den Poser steuern darf. Siehe Script Zeile 56 bis 58
Es bietet Optionen wie "jeder", die gleiche Gruppe" oder spezielle Avas die per UUID  angegeben werden.

Das Script benötigt genau wie PMAC eine Basisanimation niedrigster Priorität.  
Diese muss wie im Script angegeben heissen: "*****base_DO NOT DELETE ME!"


In der Version 3.7 werden die zum Rezzen angeforderten NPCs per Zufall ausgewählt.

Code:
// P604 PARAMOUR LINE-DANCE CONTROLLER OSSL v3.7 VSL by Tron 11.2021
// original OSSL Version by Mata Hari / Aine Caoimhe November 2014
// 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/
//
// *** Dieses Script benötigt Regionen in denen OSSL Befehle freigeschalten sind, und der Scriptbesitzer diese Rechte hat***
//
// Unbedingt darauf achten das Prim dieses Scriptes im Kontext der erlaubten Simgruppe zu rezzen,
// ansonsten kommt es zu NPC Fehlern wenn der öffentliche Zugang abgeschalten wird!
//
// Diverse Änderungen: NPCs rezzen nun Fehlerfrei auf gesperrten Sims wenn sie die erlaubte Gruppe haben
// Änderungen by Tron: Script für Funktionsfähigkeit in Sl und Opensim umgeschrieben.
// Hinzufügung eines umfangreichen Berechtigungs Systems.
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// USER SETTINGS - stuff that any user should feel comfortable setting
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
//
integer textanzeige =TRUE;                      // TRUE = show floaty text, FALSE = hide it
string  textmessage;                            // name to appear in floaty text above the line dance object. Read from Prim description
vector  textcolour =<1.000, 0.847, 0.200>;      // colour to use for the floaty text
//
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// ADVANCED USER SETTINGS - a more advanced user might want to adjust these
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
integer  debug = FALSE;
integer  maxLineLength=5;                        // maximum number of dancers allowed in a single line
float    spacingX=1.5;                           // distance between each dancer in the line (side to side)
float    spacingY=-1.5;                          // when more than 1 line is needed, how far apart to space lines
vector   p1Pos=<0.0, 0.0, 1.25>;                 // sit target position for 1st dancer - all other dancers positioned relative to this position (and centered on it) <0.0, 0.0, -2.0>;
rotation p1Rot=ZERO_ROTATION;                    // sit target rotation for 1st dancer - making this non-zero could produce unusual results!
integer  randomOrder=FALSE;                      // TRUE = play dances in random order, FALSE = play in alphabetical order
float    danceTimer=60.0;                        // how long (in seconds) to play each line dance by default before moving on to the next one (owner can also change this via dialog)
//
//
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// DON'T CHANGE ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU'RE DOING!!!!!
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
string  baseAnim="*****base_DO NOT DELETE ME!";
list    animatliste;
integer animationsnr;
string  currentAn;
list    dancerliste;              // Liste von Avataren UND NPCs auf dem Poser
string  myState="OFF";
integer myChannel;
integer handle;
integer closeHandle;
list    npcList;
integer npcToRez;
integer taenzeranzahl;            // Integer! Anzahl der aktuellen Tänzer

integer poserlinks;
integer taenzeraufposer;

key     toucher;               // UUID des Operator des Menüs

list    zugangsliste = ["2338a1d0-0d7b-4ec1-b841-029c49c9d461","8998586e-b754-4032-856f-f4f4b122eb6e"]; // UUIDS berechtigter AVAs
integer groupaccess  = TRUE;   // TRUE ore FALSE - darf die Gruppe steuern?
integer publicaccess = FALSE;  // TRUE ore FALSE - darf jeder steuern?

list    npccardlist;
list    npcnamelist;


integer berechtigungscheck(string clicker, integer selbegruppe)  // Zugangsberechtigung auswerten
     {
        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 ( (integer)personal && (!zugang));
        }
        return zugang;
     }


showMenu(key operator)
{
    myChannel=0x80000000 | (integer)("0x"+(string)operator);
    string txtDia="Danceliner Optionen Menü: ADD/Kill NPCs, Animationswechel, Ändern der Tanzdauer";
    list butDia=["ABBRUCH","NextDance","Add NPC","Kill NPC","Kill ALL","60 SEC","90 SEC","120 SEC","300 SEC","Debugmode"];
    handle=llListen(myChannel,"",operator,"");
    llDialog(operator,txtDia,butDia,myChannel);
    closeHandle=TRUE;
}


positionDancers()
{
    dancerliste=[];
    integer link=llGetNumberOfPrims();
    while (link>0)
    {
        if (llGetAgentSize(llGetLinkKey(link))!=ZERO_VECTOR) dancerliste=[]+[llGetLinkKey(link),link]+dancerliste;
        link--;
    }
    integer laufvar5;
    taenzeranzahl=llGetListLength(dancerliste);
    integer row;
    integer pos;
    integer thisLineCount;
    while (laufvar5 < taenzeranzahl)
    {
        vector thisPos=p1Pos;
        rotation thisRot=p1Rot;
        thisPos.y+=(pos*spacingX);
        thisPos.x+=(row*spacingY);
        llSetLinkPrimitiveParamsFast(llList2Integer(dancerliste,laufvar5+1),[PRIM_POS_LOCAL,thisPos,PRIM_ROT_LOCAL,thisRot]);
        thisLineCount++;
        if(thisLineCount>=maxLineLength)
        {
            thisLineCount=0;
            pos=0;
            if (row==0) row++;
            else if (row>0) row=row*-1;
            else row=(row*-1)+1;
        }
        else
        {
            if (pos==0) pos++;
            else if (pos>0) pos=pos*-1;
            else pos=(pos*-1)+1;
        }
        laufvar5 +=2;
    }
}


startDancing(key who)
{
    llRequestPermissions(who, PERMISSION_TRIGGER_ANIMATION );
    key doNotStop=llGetInventoryKey(baseAnim);
    osAvatarPlayAnimation(who,baseAnim);
    list anToStop=llGetAnimationList(who);
    integer avacount=llGetListLength(anToStop);
    while (--avacount>=0) { if (llList2Key(anToStop,avacount)!=doNotStop) osAvatarStopAnimation(who,llList2Key(anToStop,avacount)); }
    osAvatarPlayAnimation(who,currentAn);
}


nextDance()
{
    animationsnr++;
   
    if (animationsnr>=llGetListLength(animatliste))
    {
        if (randomOrder) animatliste=[]+llListRandomize(animatliste,1);
        animationsnr=0;
    }
    string nextDance=llList2String(animatliste,animationsnr);
    taenzeranzahl=llGetListLength(dancerliste);  
    if (debug) llWhisper(0,"Starting " +(string)animationsnr +". Animation:  "  +nextDance+"  Stopping: "+currentAn );
    integer i;
    while (i<taenzeranzahl)
    {
        key who=llList2Key(dancerliste,i);
        llRequestPermissions(who, PERMISSION_TRIGGER_ANIMATION );
        osAvatarStopAnimation(who,currentAn);
        osAvatarPlayAnimation(who,nextDance);
        i+=2;
    }
    currentAn=nextDance;
    positionDancers();
    llSetTimerEvent(danceTimer);
}


showText(integer anzeige)
{
    if (anzeige)
    { 
        if (myState=="ERROR") { llSetText("ERROR - owner needs to fix and reset",textcolour,1.0);}
        else llSetText(textmessage,textcolour,1.0);
    }
    else llSetText("",textcolour,0.0);
}


buildAnimList()
{
    animatliste=[];
    integer l=llGetInventoryNumber(INVENTORY_ANIMATION);
    while (--l>=0) { if (llGetInventoryName(INVENTORY_ANIMATION,l)!=baseAnim) animatliste=[]+[llGetInventoryName(INVENTORY_ANIMATION,l)]+animatliste; }
    if (llGetListLength(animatliste)==0)
    {
        llWhisper(0,"ERROR! No dance animations in inventory!");
        myState="ERROR";
    }
    else if (llGetListLength(animatliste)>1)
    {
        if (randomOrder) animatliste=[]+llListRandomize(animatliste,1);
        else animatliste=[]+llListSort(animatliste,1,TRUE);
    }
    if(debug) llSay(0,"Animations Liste: "+ (string)animatliste);
    animationsnr=-1;
}


integer buildNPClist()
{
    npccardlist = [];
    npcnamelist = [];
    integer npczahl;
    integer notecardzahl = llGetInventoryNumber(INVENTORY_NOTECARD);
    while (notecardzahl)
    {
        notecardzahl--;                                              // Anzahl ist 1 höher als Adressierung.
        string notecardname = llGetInventoryName(INVENTORY_NOTECARD,notecardzahl);
        if (llSubStringIndex(notecardname,".NPC")==0)               // Testen ob sie eine .NPC Notecard ist
        {
            string npc_name = llGetSubString(notecardname,8,-1);     // npc_namen ermitteln
            if(debug) llSay(0,"NPC Namen: " + npc_name );
            npccardlist += notecardname;                            // Liste mit NPC enthaltenden Notecards erstellen
            npcnamelist += npc_name;                                // Liste mit NPC Namen erstellen
            npczahl++;
        }
    }
    if(debug) llSay(0,"NPC Zahl: "+ (string)npczahl +"  NPC Liste: "+(string) npcnamelist);
    return (npczahl);
}


npc_alive_check()                                                    // Check ob die vorher gerezzten NPCs noch leben
{
    integer npczahl = llGetListLength(npcList);
    list    restnpcList = [];
    integer laufvar;
    while (laufvar < npczahl)
    {
        key npctocheck = llList2Key(npcList,laufvar);                // Check ob NPC Key noch vorhanden- als Lebenszeichen
        if (osIsNpc(npctocheck)) { restnpcList += npctocheck;}        // Rest NPC Liste erstellen
        laufvar++;
    }
    npcList = [] + restnpcList;                                        // NPC Liste aktualisieren
    if(debug) llSay(0,"Lebendige NPCs: "+ (string)npczahl +" UUID Liste gerezzter NPCs: "+(string) npcList);
}


key npc_rezzer(key sittarget, vector npcPos, string npcnotecard, string npc_name)    // NPC im GRUPPENKONTEXT rezzen, damit der auf Sims geht die öffentlich gesperrt, aber für Gruppe frei ist
{
    key npctorez = osNpcCreate(npc_name, "" , npcPos, npcnotecard, 8 | OS_NPC_NOT_OWNED | OS_NPC_SENSE_AS_AGENT | OS_NPC_OBJECT_GROUP );
    llSleep(0.5);
    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
    }
    if(debug) llSay(0," NPC gerezzt: " + npc_name + " mit UUID: " + (string)npctorez);
    llSleep(1.0);
    osAvatarPlayAnimation(npctorez,currentAn);
    return npctorez;
}


npc_rez_selector(list npccardlist, list npcnamelist )    // NPC auswählen der gerezzt werden soll
{
    key sittarget = llGetKey();
    vector npcPos = llGetPos();
    integer randomnpc = llFloor(llFrand(llGetListLength(npccardlist)));
    string  npcnotecard = llList2String(npccardlist,randomnpc);
    string  npc_name    = llList2String(npcnamelist,randomnpc);
    if(debug) llSay(0,"NPC Selector: NPC "+ (string) llGetListLength(npccardlist) + " per Zufall gewählt: " + (string)randomnpc + "  " + npc_name);
    key npc = npc_rezzer(sittarget, npcPos, npcnotecard, npc_name);
    npcList=[]+npcList+[npc];
}


default
{
    on_rez (integer foo) {llResetScript();}


    state_entry()
    {
        poserlinks = llGetNumberOfPrims();  // Primzahl leeren Posers ermittlen und speichern
        textmessage  = llGetObjectDesc();   // Prim Beschreibungsfeld auslesen
        llSetSitText(textmessage);
        llSitTarget(<0.0,0.0,0.0001>,ZERO_ROTATION);
        if (llGetInventoryType(baseAnim)!=INVENTORY_ANIMATION) {llWhisper(0,"ERROR! Unable to locate the base animation which MUST be in the ball."); myState="ERROR";}
        buildAnimList();
        integer npczahl = buildNPClist();
        if (debug) llSay(0, "Anzahl gefundenen NPCs: "+(string)npczahl );
        if (myState!="ERROR")
        {
            myState="READY";
            dancerliste=[];
            animationsnr=0;
            currentAn=llList2String(animatliste,animationsnr);
        }
        showText(textanzeige); // dient auch zum Löschen
    }

   
    changed (integer change)
    {
        if (change & CHANGED_REGION_START) llResetScript();
        if (change & CHANGED_OWNER) llResetScript();
        if (change & CHANGED_LINK)
        {
            list newDancerslist;
            integer aktlinkzahl=llGetNumberOfPrims();
            taenzeraufposer = aktlinkzahl - poserlinks;  // Tänzerzahl aus Primdifferenz ermitteln und speichern
            if(taenzeraufposer) {showText(FALSE);} else {if(textanzeige) showText(TRUE);}
            if(debug) llWhisper(0,"Aktuelle Tänzerzahl auf Poser: "+ (string)taenzeraufposer);
            npc_alive_check(); // Testen ob NPCs verschwunden sind, und NPC Liste bereinigen.
            while (aktlinkzahl>0)  // Taenzer abzählen und Liste erstellen
            {
                if (llGetAgentSize(llGetLinkKey(aktlinkzahl))!=ZERO_VECTOR) newDancerslist=[]+newDancerslist+[llGetLinkKey(aktlinkzahl),aktlinkzahl];
                aktlinkzahl--;
            }
            taenzeranzahl =llGetListLength(dancerliste);      
            integer laufvar1;
            while (laufvar1 < taenzeranzahl)
            {
                key who=llList2Key(dancerliste,laufvar1);
                integer ind=llListFindList(newDancerslist,[who]);
                if (ind>=0) // jemand sitzt neu
                {
                    dancerliste=[]+llListReplaceList(dancerliste,[llList2Integer(newDancerslist,ind+1)],laufvar1+1,laufvar1+1);
                    newDancerslist=[]+llDeleteSubList(newDancerslist,ind,ind+1);
                }
                else // jemand steht auf
                {
                    osAvatarPlayAnimation(who,"stand");
                    osAvatarStopAnimation(who,currentAn);
                    osAvatarStopAnimation(who,baseAnim);
                    dancerliste=[]+llDeleteSubList(dancerliste,laufvar1,laufvar1+1);
                }
                laufvar1+=2;
            }

            integer neutaenzer =llGetListLength(newDancerslist);  // Anzahl neuer Tänzer ermitteln- Achtung gibt doppelte Zahl aus.
            integer laufvar2 =0;

            while (laufvar2<neutaenzer)
            {
                startDancing(llList2Key(newDancerslist,laufvar2));
                laufvar2+=2;
            }

            dancerliste=[]+dancerliste+newDancerslist;
            if (llGetListLength(dancerliste)==0) { myState="READY"; llSetTimerEvent(0.0);}
            else if (myState=="READY") { myState="ON"; nextDance();}   // have to advance dance to keep them all synched and to trigger positioning
        }
    }


    touch_start(integer dummy)  // Menü Zugriff regeln
     {
        toucher = llDetectedKey(0);
        integer gruppe = llDetectedGroup(0);
        integer access;
        access = berechtigungscheck(toucher,gruppe);
        if(access) {showMenu(toucher); if(debug) llInstantMessage(toucher,"Menü Zugriff gewährt");}
     }


    listen(integer channel, string name, key who, string message)
    {
        llListenRemove(handle);
        if(debug) llSay(0,"Message enpfangen: " + message);
        closeHandle=FALSE;
   
        if (message=="ABBRUCH") return;

        else if (message == "NextDance") {if (myState=="ON") nextDance();}

        else if (message == "Add NPC")
        {
            if (llGetInventoryNumber(INVENTORY_NOTECARD)<1) { llInstantMessage(toucher,"I can't find any NPC notecards to use!");}
            else { npc_rez_selector(npccardlist, npcnamelist ); llSetTimerEvent(2.0); ;
            }
        }

        else if (message == "Kill NPC")
        {
            osNpcRemove(llList2Key(npcList,-1));
            npcList=[]+llDeleteSubList(npcList,-1,-1);
            showMenu(toucher);
        }

        else if (message == "Kill ALL")
        {
            while(llGetListLength(npcList))
            {
                osNpcRemove(llList2Key(npcList,-1));
                npcList=[]+llDeleteSubList(npcList,-1,-1);
            }
        }

        else if (llSubStringIndex(message," SEC")>=0)
        {
            danceTimer=(float)(llGetSubString(message,0,llSubStringIndex(message," MIN")-1));
            llInstantMessage(toucher,"Animations Dauer ist nun auf " + message + " gestellt");
            if (myState=="ON")
            {
                llSetTimerEvent(danceTimer);
                nextDance();
            }
            showMenu(toucher);
        }

        else if (message == "Debugmode") { debug = !debug; llInstantMessage(toucher,"Debugmodus [1 an / 0 aus] = "+(string)debug);}
    }
   
   
    timer()
    {
        if(closeHandle)
        {
            closeHandle++;
            if (closeHandle>2)
            {
                closeHandle=0;
                llInstantMessage(toucher, "Dialog has timed out...touch me again to re-active the dialog menu");
                llListenRemove(handle);
            }
        }
        if (myState=="ON") { nextDance(); }
        else llSetTimerEvent(0.0);
    }
}




ANWENDUNG:

- Rezze einen Würfel
- lege die Basisanimation "*****base_DO NOT DELETE ME!" hinein.
- füge die gewünschten Soll Animationen hinzu.
- (optional) dann die vorbereiteten NPC Karten (-Sets)
dann das Script
Änder die Primbeschreibung von "touch" auf "sit" damit man den Stuhl angezeigt bekommt wenn man mit dem Mauscursor über das Prim fährt.


Viel Spaß damit Tron
Zitieren


Nachrichten in diesem Thema
Paramour Line Dancer Script für Opensim in OSSL Regionen mit NPC Unterstützung - von Tron Mcp - 19.07.2022, 20:01

Gehe zu:


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