• Bitte schaltet eure Ad Blocker aus. SLinfo kann nur betrieben werden, wenn es durch Werbung Einnahmen erzielt. Vielen Dank!!
  • Wir freuen uns, wenn du dich in unserem Forum anmeldest. Bitte beachte, dass die Freigabe per Hand durchgeführt wird (Schutz vor Spammer). Damit kann die Freigabe bis zu 24 Stunden dauern.
  • Wir verwenden Cookies, um Inhalte und Anzeigen zu personalisieren, Funktionen für soziale Medien anbieten zu können und die Zugriffe auf unsere Website zu analysieren. Sie geben Einwilligung zu unseren Cookies, wenn Sie unsere Webseite weiterhin nutzen.

Tutorial: Trinkgeld-Script bauen (Tipjar)

Calyn Canning

Aktiver Nutzer
Ein Trinkgeld-Script (in Secondlife auch bekannt unter dem Namen Tip-Jar) sammelt für eine bestimmte Person Trinkgelder und zeigt die Summe dieser Gelder über dem Objekt an.

Voraussetzungen: Bauen einer Box in Secondlife sowie das Anwählen des Reiters Inhalt/Content im Baumenu für dieses Objekt.

Ziel des Tutorials: Verstehen, was Zustände sind, wie der Grundaufbau eines Scriptes ist, was Ereignisse sind und wie Funktionen und Variablen benutzt werden.

1. Zuerst erstellen wir in Secondlife eine Box und wählen über das Baumenu unter dem Reiter Inhalt/Content die Option New Script. Wir werden daraufhin im Chatfenster mit den Worten "Hello, Avatar" von unserem neuen Script begrüßt. Manchmal kann es vorkommen, dass es eine kleine Verzögerung gibt, also nicht mehrmals drücken, sondern ein paar Sekunden zur Not gedulden.

2. Durch einen Klick mit der rechten Maustaste auf das Script, kann das Script umbenannt, geöffnet und gelöscht werden. Wir geben dem Script erst einmal einen Namen und öffnen es anschliessend. Nun sehen wir folgendes vor uns:

Code:
default
{
	state_entry()
	{
		llSay(0, "Hello, Avatar!");
	}
	touch_start(integer total_number)
	{
		llSay(0, "Touched!");
	}
}

3. Zuerst soll der oben stehende Programmcode kurz erklärt und verstanden werden, bevor wir mit der Erstellung des Trinkgeld-Scriptes fortfahren. Wer den oben stehenden Programmcode jedoch bereits versteht, kann in Punkt 7 weiterlesen.

4. Das Wort default steht für einen Zustand des Scriptes, genauer gesagt den wichtigsten Zustand des Scriptes. Dieser muss verpflichtend in jedem Script vorkommen und wird beim Starten des Scriptes automatisch aktiviert. Der Programmierer eines Scriptes kann auch eigene Zustände definieren, wie wir später auch an unserem Trinkgeld-Script feststellen werden. Bei der Secondlife-Programmierung gilt wie bei einigen anderen Programmiersprachen auch die Klammerung von Programmcode-Blöcken durch die geschweiften Klammern "{" und "}".

5. Innerhalb des Programmcode-Blocks des Zustandes default gibt es zwei Ereignisse, diese sind state_entry() und touch_start(integer total number). Das Ereignis state_entry() findet statt, sobald das Script in den Zustand geht, von dem das Ereignis umschlossen wird, in diesem Fall also beim Eintritt des Zustandes default. Das Ereignis touch_start(integer total_number) ist das Ereignis, welches beim Anklicken eines Objektes gestartet wird, in den runden Klammern wird dabei ein Parameter des Type integer (eine ganze Zahl) mit dem Namen total_number übergeben, ein Parameter stellt eine Zusatzinformation dar, welche dem Ereignis mitgegeben wird, in diesem Fall die Anzahl der das Objekt anklickenden Avatare.

6. In beiden Zuständen wird innerhalb der Programmcode-Blöcke des Ereignisses lediglich eine Funktion aufgerufen. Diese Funktion ist eine Standardfunktion von Secondlife, man erkennt sich an den beiden vorangestellten beiden kleinen "L" im Befehl llSay, welcher das Script zum Sprechen bringen kann. Auch die Funktion hat 2 solcher Parameter, der erste Parameter ist der Chat-Kanal, auf dem die Nachricht gesendet wird und der zweite Parameter enthält die Nachricht.

7. Wir wollen nun dazu übergehen und das Script an unsere Bedürfnisse anzupassen. Zuerst überlegen wir uns hierzu eine passende Struktur, welche anschließend programmiert werden soll. Das folgende Script macht noch gar nichts, es wird lediglich die spätere Struktur vorgegeben und mit Kommentaren (diese werden eingeleitet mit //) die Abläufe innerhalb dieser Struktur beschrieben.

Code:
default
{
	on_rez(integer start_param)
	{
		// Beim Aufbauen der Box in den Ausgangszustand bringen
	}
	
	state_entry()
	{
		// Informationen abfragt und für die weitere Verwendung speichern
	}
}

state inaktiv
{
	state_entry()
	{
		// Informationstext über der Box teilt mit, dass das Script nicht aktiv ist
	}
	
	touch_start(integer total_number)
	{
		// Nur der Besitzer der Box darf das Script mit einem Klick aktivieren
	}
}

state aktiv
{
	state_entry()
	{
		// Informationstext über der Box teilt mit, dass das Script aktiv ist
	}
	
	touch_start(integer total_number)
	{
		// Nur der Besitzer des Objektes darf das Script mit einem Klick deaktivieren
	}
	
	money(key giver, integer amount)
	{
		// Bei Erhalt eines Trinkgeldes soll dem Spender gedankt werden und die neue Summe angezeigt werden
	}
}

8. Dem aufmerksamen Betrachter wird nun auffallen, dass es genau drei dieser Zustände gibt, den bereits vorgestellten Zustand default, welcher verpflichtend in jedem Script vorkommen muss, sowie die beiden selbst definierten Zustände inaktiv und aktiv. Wir benutzen den Zustand default um am Anfang einige wichtig Informationen zu bekommen und zu speichern. Im Anschluss daran soll das Script in den Zustand inaktiv wehseln bis der Besitzer es mit einem Klick aktiviert. Von da an soll es Spenden sammeln bis der Besitzer es mit einem erneuten Klick ausschaltet (in den Zustand inaktiv bringt).

9. An dieser Stelle wollen wir uns nun Gedanken machen über benötigte Variablen, die wir im Script benötigen. In Variablen können wir Informationen speichern. Für unser Trinkgeld Script wollen wir auf folgende Informationen zurückgreifen: Name des Besitzers und die Gesamtsumme der Spenden. Beim Namen des Besitzers handelt es sich um eine Zeichenkette also um einen string, bei der Gesamtsumme der Spenden um eine ganze Zahl, also vom Typ integer, damit wir auch damit rechnen können. Wir machen diese beiden Variablen vor dem Zustand default bekannt. Dies hat den Vorteil, dass sie überall im Script benutzt werden können.

Code:
string 	besitzername;
integer gesamtsumme;

10. Wir wollen nun den Zustand default programmieren. Eine Erklärung zu den einzelnen Funktionen ist diesen als Kommentar vorangestellt.

Code:
default
{
	// Das Ereignis on_rez tritt ein, sobald die Box "gerezzt" (aufgestellt) wird.
	on_rez(integer start_param)
	{
		// llResetScript(); startet das Script neu.
		llResetScript();
	}
	
	state_entry()
	{
		// Name des Besitzers in die Variable besitzername speichern
		// Hierzu ermitteln wir den Key (Schlüssel) des Besitzers mit der Funktion llGetOwner();
		key besitzerkey = llGetOwner();
		// Nun ermitteln wir aus dem Key den Namen des Besitzers mit der Funktion llKey2Name();
		besitzername = llKey2Name(besitzerkey);
		// Wir springen in den Zustand inaktiv
		state inaktiv;
	}
}

11. Der Zustand default ist damit nun fertig ausprogrammiert und es geht nun an das "Eingemachte". Wir wollen uns zuerst den Zustand inaktiv vornehmen und diesen programmieren und im Anschluss die eigentliche Funktionalität des Trinkgeld-Scriptes hinzufügen.

Code:
state inaktiv
{
	state_entry()
	{
		// Informationstext über der Box teilt mit, dass das Script nicht aktiv ist
		// Die Funktion llSetText() erwartet drei Parameter, den angezeigten Text, einen Farbvektor und die Stärke des Textes
		llSetText("Trinkgelder inaktiv",<1,1,1>,1);
		// Der Gesamtbetrag für die Trinkgelder wird an dieser Stelle auf 0 gesetzt
		gesamtsumme = 0;
	}
	
	touch_start(integer total_number)
	{
		// Nur der Besitzer der Box darf das Script mit einem Klick aktivieren
		// Wir ermitteln den Key des Besitzers und den Key des Klickenden und vergleichen beide
		key besitzer = llGetOwner();
		key klickender = llDetectedKey(0);
		// Vergleichen und wenn gleich in den Zustand aktiv springen
		if (besitzer == klickender)
			state aktiv;
	}
}

12. Mit diesem Programmcode haben wir auch den inaktiven Status des Trinkgeld-Scriptes programmiert. Was haben wir genau gemacht? Zuerst haben wir bei der Anwahl des Zustandes einen weissen (<1,1,1> ergibt weiss) Text über dem Objekt auf "Trinkgelder inaktiv" gesetzt. Dann haben wir die Gesamtsumme an Trinkgeldern auf 0 gesetzt. Für das Ereignis, dass das Objekt angeklickt wird, haben wir überprüft ob der Besitzer geklickt hat und wenn dies der Fall ist, sind wir in den Zustand aktiv gesprungen. Dieser muss nun noch programmiert werden.

Code:
state aktiv
{
	state_entry()
	{
		// Informationstext über der Box teilt mit, dass das Script aktiv ist
		llSetText("Trinkgelder " + besitzername + "
Bisher noch nichts gespendet!",<1,1,1>,1);
	}
	
	touch_start(integer total_number)
	{
		// Nur der Besitzer der Box darf das Script mit einem Klick deaktivieren
		key besitzer = llGetOwner();
		key klickender = llDetectedKey(0);
		// Vergleichen und wenn gleich in den Zustand inaktiv springen
		if (besitzer == klickender)
			state inaktiv;
	}
	
	money(key giver, integer amount)
	{
		// Bei Erhalt eines Trinkgeldes soll dem Spender gedankt werden und die neue Summe angezeigt werden
		llSay(0,"Danke fuer deine Spende, " + llKey2Name(giver) + "!");
		// Trinkgelder addieren
		gesamtsumme += amount;
		// Informationstext aktualisieren
		llSetText("Trinkgelder " + besitzername + "
Bisher " + (string)gesamtsumme + " L $ gespendet!",<1,1,1>,1);
	}
}

13. Wenn du nun noch nicht aufgegeben hast, dann kannst du nun getrost aufatmen, denn wir sind fast fertig. Wir haben nun den aktiven Zustand programmiert, hierbei gibt es im Gegensatz zu den vorangegangenen Zuständen nicht zwei sondern drei Ereignisse. Zusätzlich zu den Ereignissen state_entry() und touch_start(integer total_number) gibt es hier noch das Ereignis money(key
 
@Calyn Canning
Absolut super gemacht!

Aber eine Anmerkung zum Script hätte ich noch: Es ist überflüssig, im jeweiligen Touch-Event den Owner-Key abzufragen. Das kann man einmal im Default-State-Entry tun und den Wert in einer globalen Variablen speichern.

@MODs
Wäre dieser Beitrag nicht auch etwas fürs Wiki?
 
Sylvie Munro schrieb:
@Calyn Canning
Absolut super gemacht!

Aber eine Anmerkung zum Script hätte ich noch: Es ist überflüssig, im jeweiligen Touch-Event den Owner-Key abzufragen. Das kann man einmal im Default-State-Entry tun und den Wert in einer globalen Variablen speichern.
Danke Sylvie,

ich bin mir dessen zwar bewusst, dennoch wollte ich es so einbasteln. Genauso unnötig sind die lokalen Variablen, da ich auch direkt in der IF-Abfrage prüfen könnte, das Tutorial richtet sich aber an den totalen Anfänger und soll daher möglichst in kleinen Schritten zum Ergebnis führen. Ich werde es daher mal in dieser Variante lassen.

Lieben Gruß
Calyn
 
Hurra! Genauso was hab ich gesucht und endlich gefunden!
Denn werd ich alsbald wohl auch mal versuchen, mir ein Script zu häkeln...
 
/me hat's abgekupfert und in ein hübsches Töpfchen verpflanzt

Gutes Tutorial! *****

... jetzt fehlt mir nur noch die Idee dazu wo ich es aufstellen kann ;-P
 
Vielen Dank für eure Rückmeldungen zum Tutorial. Ich hoffe, es hat dem ein oder anderen geholfen, einen Einstieg in die Programmierung in SL zu finden.

Da ich im Moment nicht weiss, was man sich noch als Tutorial im Bereich Scripte vorstellen kann, meine Frage: Was wollt ihr außer Maoam? :wink:

Calyn
 
Calyn Canning schrieb:
Da ich im Moment nicht weiss, was man sich noch als Tutorial im Bereich Scripte vorstellen kann, meine Frage: Was wollt ihr außer Maoam? :wink:

SNICKERS :mrgreen:


Hmm mach doch einfach mit dem selben Script weiter und erweiter es um neue nützliche Funktionen. Z.B. Gruppenabfrage. , Tip Aufteilung usw
 
Vielen Dank für die Rückmeldung, Doktor. Und hier gibts auch direkt etwas um die Ohren:

Bisher ist das Script konzipiert als einfaches Trinkgeldscript, in das sich lediglich der Besitzer einloggen und so Trinkgelder annehmen kann. Will man es für den Clubeinsatz mit mehreren Mitarbeitern einsetzen, so ist etwas Anpassungsarbeit notwendig.

Hierzu passen wir die Struktur unseres Scriptes an. Ein Vergleich mit der bisherigen Struktur folgt unter dem Code.

Code:
default 
{ 
   on_rez(integer start_param) 
   { 
      // Beim Aufbauen der Box in den Ausgangszustand bringen 
   } 
    
   state_entry() 
   { 
      // Informationen abfragen und für die weitere Verwendung speichern
      // Erlaubnis abfragen, Geld zu nehmen, um den Mitarbeiteranteil zu bezahlen
   } 

   run_time_permissions(integer permissions)
   {
       // Wurde die Erlaubnis gegeben, dann in den nächsten Zustand springen
   }
} 

state inaktiv 
{
   on_rez(integer start_param) 
   { 
      // Beim Aufbauen der Box in den Ausgangszustand bringen 
   } 
    	 
   state_entry() 
   { 
      // Informationstext über der Box teilt mit, dass niemand eingeloggt ist
   } 
    
   touch_start(integer total_number) 
   { 
      // Nur Mitglieder der Gruppe des Trinkgeldobjektes dürfen sich anmelden
   } 
} 

state aktiv 
{
   on_rez(integer start_param) 
   { 
      // Beim Aufbauen der Box in den Ausgangszustand bringen 
   } 
    	 
   state_entry() 
   { 
      // Informationstext über der Box teilt mit, dass das Script aktiv ist 
   } 
    
   touch_start(integer total_number) 
   { 
      // Nur der Besitzer des Objektes und der angemeldete Mitarbeiter darf Abmelden 
   } 
    
   money(key giver, integer amount) 
   { 
      // Bei Erhalt eines Trinkgeldes soll:
      // dem Spender gedankt werden
      // die neue Summe angezeigt werden
      // den Anteil des Mitarbeiters auszahlen
   } 
}

Wie man sieht, gibt es nun einige Änderungen im Script. Zuerst einmal wird die Berechtigung benötigt, Geld vom Besitzer des Trinkgeldscriptes zu nehmen um den Anteil des Mitarbeiters zu bezahlen. Dies findet sich im default-State wieder.

Damit es nicht zu Unstimmigkeiten kommt und Mitarbeiter ihren Anteil nicht ausgezahlt bekommen, soll explizit nur dann das Trinkgeldscript nutzbar sein, wenn diese Erlaubnis gegeben wurde. Das stellen wir mit dem Ereignis run_time_permissions(integer permissions) fest.

Im inaktiven Zustand wird die Abfrage nach dem Besitzer des Objektes als anmeldeberechtigter Nutzer erweitert auf die Gruppe, zu der das Script gehört. Bevor Verwirrungen aufkommen sei angemerkt, dass ein Script keiner Gruppe angehören kann sondern nur das Objekt, welches das Script enthält, der Einfachheit halber werde ich aber weiter von der Gruppe des Scriptes reden.

Der aktive Zustand hat zwei Änderungen gegenüber der bisherigen variante. Zuerst einmal wurde auch das Ausloggen erweitert und zwar um die Möglichkeit als Besitzer den aktiven Avatar abzumelden und als Mitarbeiter sich selber abzumelden. Die zweite Änderung ist die Auszahlung des Anteils an den Mitarbeiter, weil das Geld standardmäßig an den Besitzer des Objektes geht. Genau dafür haben wir vorher das Script mit der Erlaubnis versorgt, Auszahlungen vorzunehmen.

Beginnen wir nun mit der eigentlichen Programmierung. Wir wollen mit dem default-State anfangen und uns um das Starten und die Einrichtung des Scriptes kümmern. Zuvor definieren wir einige globale Variablen, die für das Script sinnvoll sind. In diesem Tutorial werden wir den Anteil für den Besitzer nicht über eine Eingabe oder ein Menu abfragen sondern fest in das Script schreiben, für ein weiteres Tutorial ist das ein Menu zur Einstellung des Trinkgeldscriptes gedacht.

Code:
string benutzerName;
key benutzerSchluessel;
integer einnahmen;
// Der Besitzeranteil wird in Prozent angegeben und muss zwischen 0 und 100 sein.
integer besitzerAnteil = 10;

default 
{ 
   on_rez(integer start_param) 
   { 
      llResetScript();
   } 
    
   state_entry() 
   { 
		if ((besitzerAnteil >= 0) && (besitzerAnteil <= 100))
			llRequestPermissions(llGetOwner(),PERMISSION_DEBIT);
   } 

   run_time_permissions(integer permissions)
   {
		if (permissions & PERMISSION_DEBIT)
			state inaktiv;	
   }
}

Was haben wir hier gemacht? Zuerst haben wir eine String-Variable definiert, die den Namen des aktuell angemeldeten Benutzers aufnehmen soll. Eine zweite Variable des Typs key nimmt den eindeutigen Schlüssel des Benutzers auf. Eine Integer-Variable dient der Speicherung der Summe der bisherigen Trinkgelder.

Bei der letzten Variable des Type integer mit dem Namen besitzerAnteil handelt es sich um die einzige Variable der an dieser Stelle direkt ein Wert zugewiesen wird. Es handelt sich um den prozentualen Anteil der Einnahmen, der beim Besitzer verbleibt und nicht an den Mitarbeiter ausgezahlt wird.

Das Ereignis on_rez(integer start_param) wird nicht weiter betrachtet, es wurde bereits im ersten Teil erläutert und dient lediglich dem Zurücksetzen des Objektes in den Ausgangszustand.

Im Ereignis state_entry() wird sichergestellt, dass der Wert von besitzerAnteil einen gültigen Prozentwert darstellt. Ist das der Fall, so fragt das Script mit dem Befehl llRequestPermissions(llGetOwner(),PERMISSION_DEBIT); den Besitzer (llGetOwner()) nach der Erlaubnis, Geld auszuzahlen (PERMISSION_DEBIT).

Das neu eingeführte Ereignis run_time_permissions(integer permissions) wird jedes Mal dann aufgerufen, wenn sich die Berechtigungen des Scriptes ändern.

Wenn also die Berechtigung gegeben wurde, so wird hier mit der if-Abfrage kontrolliert, ob die entsprechende Berechtigung gegeben wurde. Es handelt sich hier um einen Vergleich auf Bit-Ebene, was man am &-Zeichen in der Klammer sieht. Ein kleines Beispiel soll das erklären. Jede Berechtigung hat einen Wert, das ist für PERMISSION_DEBIT der Wert 2, für PERMISSION_TAKE_CONTROLS der Wert 4 und beispielsweise für PERMISSION_TRIGGER_ANIMATION der Wert 16. Diese Werte sind alle ein Vielfaches der Zahl 2 und das ist dem Binärsystems des Computers geschuldet.

Hat das Script lediglich die Berechtigung zur Auszahlung von Geld, so hat die Variable permissions den Wert 2. Bekommt sie nun die Erlaubnis dazu, einen Avatar zu animieren (PERMISSION_TRIGGER_ANIMATION) so wird der Wert 16 hinzugezählt, also haben wir in der Variable 18 stehen. Das sieht im Binärsystem wie folgt aus: 10010

Der Binärvergleich im Script prüft also auf PERMISSION_DEBIT und vergleicht dazu lediglich die zweitletzte Stelle auf eine 1. Vorteil dieser Technik ist, man kann in einer speicherfreundlichen Integervariable nicht nur eine sondern ganz viele Einstellungen des Typs ja/nein hinterlegen.

Wurde also die Berechtigung gegeben, so springen wir in den State mit dem Namen inaktiv.

Code:
state inaktiv 
{
   on_rez(integer start_param) 
   { 
      llResetScript(); 
   } 
    	 
   state_entry() 
   { 
      llSetText("Trinkgelder
Derzeit kein Mitarbeiter angemeldet.",<1,1,1>,1);
   } 
    
   touch_start(integer total_number) 
   { 
		if (llSameGroup(llDetectedKey(0)))
		{
			benutzerSchluessel = llDetectedKey(0);
			benutzerName = llKey2Name(benutzerSchluessel);
			einnahmen = 0;
			llInstantMessage(llDetectedKey(0),"Du wurdest angemeldet!");			
			state aktiv;		
		}
		else
			llInstantMessage(llDetectedKey(0),"Nur für Mitglieder der korrekten Gruppe!");
   } 
}

Der oben stehende Code zeigt das Anmelden des Mitarbeiters. Auch hier wird wieder überprüft, ob das Objekt gerade in der Welt erstellt(gerezzt) wurde. Dies ist der Tatsache geschuldet, dass man ein Objekt egal in welchen Zustand das Script sich befindet aufnehmen und wieder ablegen kann.

Im Ereignis state_entry() wird ein beschreibender Text in weisser Farbe (<1,1,1>) mit vollem Alphawert (0 = Minimum, 1 = Maximum) gesetzt.

Das Ereignis touch_start(integer total_number) registriert, wenn eine Person auf das Objekt klickt, in dem sich das Trinkgeldscript befindet. Mit der Funktion llSameGroup(llDetectedKey(0)) fragen wir ab, ob die diese Ereignis auslösende Person zur selben Gruppe wie das Script gehört. Ist das der Fall, so speichern wir ihren Schlüssel, den Namen, setzen die Einnahmen auf 0, geben dem Benutzer eine Bestätigung und wechseln in den State aktiv. Andernfalls geben wir über Befehl llInstantMessage(llDetectedKey(0),"Nur für Mitglieder der korrekten Gruppe!"); dem Avatar die Nachricht, dass er nicht die korrekte Gruppe aktiv hat.

Der wichtigste Teil des Trinkgeldscriptes ist der State aktiv, den wir nun vervollständigen wollen.

Code:
state aktiv 
{
   on_rez(integer start_param) 
   { 
      llResetScript();
   } 
    	 
   state_entry() 
   { 
      llSetText("Trinkgelder
" + benutzerName + " angemeldet.
 Bisher wurden " + (string)einnahmen + " gespendet!",<1,1,1>,1);
   } 
    
   touch_start(integer total_number) 
   { 
		if ((llDetectedKey(0) == llGetOwner()) || (llDetectedKey(0) == benutzerSchluessel))
		{
			llInstantMessage(llDetectedKey(0),"Du wurdest abgemeldet!");
			state inaktiv;
		}
   } 
    
   money(key giver, integer amount)
   {
	   llSay(0,"Danke für das Trinkgeld, " + llKey2Name(giver) + "!");
	   einnahmen += amount;
       llSetText("Trinkgelder
" + benutzerName + " angemeldet.
 Bisher wurden " + (string)einnahmen + " gespendet!",<1,1,1>,1);
	   integer prozentFuerMitarbeiter = 100 - besitzerAnteil;
	   integer geldFuerMitarbeiter = llCeil((float)amount * prozentFuerMitarbeiter / 100);
       if (geldFuerMitarbeiter > 0)
		   llGiveMoney(benutzerSchluessel,geldFuerMitarbeiter);	
   } 
}

Im Ereignis state_entry() erweitern wir den Text des Trinkgeldscriptes mit dem Namen des angemeldeten Mitarbeiters und des bisher erhaltenen Betrages. Das Ereignis touch_start(integer total_number) überprüft ob es sich bei dem klickenden Avatar um den angemeldeten Mitarbeiter oder den Besitzer handelt und meldet in diesem Fall den angemeldeten Mitarbeiter ab.

Beim wichtigsten Ereignis des Trinkgeldscriptes - wie sollte es auch anders sein - geht es ums Geld. Das Ereignis money(key giver, integer amount) hat drei Aufgaben:

Es bedankt sich beim Spender für das Trinkgeld mit dem Befehl llSay(0,"Danke für das Trinkgeld, " + llKey2Name(giver) + "!"); und addiert mit dem Befehl einnahmen += amount; den bisherigen Einnahmen den neuen Betrag hinzu, um diese im Anschluß als neuen Text anzuzeigen. Soweit unterscheidet das Trinkgeldscript sich nicht von dem aus dem ersten Teil, doch das ändert sich in den kommenden drei Befehlszeilen.

In einer neuen Variable mit der Bezeichnung prozentFuerMitarbeiter errechnen wir, welchen Anteil der Mitarbeiter bekommt. Das ist einfachste Mathematik: Das Ganze(100) abzüglich das, was der Besitzer behält, ergibt den Rest.

Mit Hilfe dieser errechneten Zahl bestimmen wir den zu zahlenden Betrag an den Mitarbeiter. Mit Hilfe der Umformung in einen Float-Wert(Fließkommazahl) und der Aufrundung nach der Berechnung durch den Befehl llCeil() verhindern wie, dass bei zu kleinen Trinkgeldbeträgen für den Mitarbeiter nicht einmal 1 Lindendollar übrig bleibt.

Trotz allem benötigen wir eine Abfrage ob
 
Calyn du bist der helle Wahnsinn :)

Gleich mal positiv bewertet.

Läuft eigentlich noch das Hall of Fame Voting ??

Jetzt fehlt dem ganzen eigentlich nur noch ein Scanner das der Mitarbeiter auch wirklich anwesend ist und es ist " DAS ULTIMATIVE TIPJAR SCRIPT ÜBERHAUPT " :)
 
Die Idee mit dem Scanner werde ich im nächsten Teil aufgreifen, das ist ja ne recht einfache Angelegenheit. Ebenfalls hinkommen soll dann ein Menu, mit dem man den Besitzeranteil regulieren kann.

Calyn
 
Hallo,

ich bin es mal wieder und ich hab auch etwas mitgebracht. Weder in einem Sack noch kam ich durch den kamin, aber ich hoffe, es gefällt trotzdem. :lol:

Ich habe die Idee von Daktor Schnyder aufgegriffen und das Trinkgeld-Script erweitert um die Möglichkeit, bei Mitarbeitern die ihren Arbeitsplatz verlassen, sie automatisch auszuloggen und so das Trinkgeld-Script für andere Mitarbeiter nutzbar zu machen.

Viel ändern müssen wir dazu nicht. Wir brauchen eine Variable, die den Radius angibt, in dem sich der Mitarbeiter bewegen darf, dass er als anwesend gewertet wird. Ich habe diese "scannerReichweite" genannt.

In die Variable wollen wir 0 eintragen, falls wir die Funktion nicht benutzen wollen, oder aber eine Zahl zwischen 1 und 96, falls wir die Funktion mit einer von uns ausgesuchten Reichweite nutzen wollen. Warum 96? Die Antwort ist ganz einfach, weil Secondlife einen Scanner mit größerer Reichweite nicht erlaubt.

Code:
// Die Reichweite des Scanners für den automatischen Logout
// 0 = ausgeschaltet
// 1 - 96 = Meter Reichweite
integer scannerReichweite = 30;

Als nächstes müssen wir lediglich den Status aktiv anpassen, weil hier der Scanner erst in Erscheinung tritt. Sobald das state_entry() Ereignis ausgelöst wird, hat sich ein Mitarbeiter angemeldet. Wir starten dann, falls eine Reichweite größer 0 angegeben wurde, den Scanner:

Code:
	state_entry()
	{
		llSetText("Trinkgelder
" + benutzerName + " angemeldet.
 Bisher wurden " + (string)einnahmen + " gespendet!",<1,1,1>,1);
		if (scannerReichweite > 0)
			llSensorRepeat(benutzerName,NULL_KEY,AGENT,(float)scannerReichweite,PI,(float)60);
	}

Die Argumente mit denen der Scanner gestartet wird, wollen wir kurz durchgehen. Das erste Argument ist ein String und enthält den Namen des Mitarbeiters, dessen Anwesenheit wird überprüfen wollen. Das zweite Argument ist ein Key nach dem gesucht werden kann, wir suchen aber nach dem Namen des Mitarbeiters.

Im dritten Argument kann die Art des gesuchten Objektes angegeben werden. Wir haben hier AGENT eingetragen, weil wir einen Avatar suchen, alternativ kann man auch nach Objekten mit und ohne laufendem Script suchen.

Das vierte Argument ist die Reichweite in der nach dem Avatar gesucht werden soll. Weil wir diese variable aber als Integer definiert haben, müssen wir sie an dieser Stelle in das geforderte Format (Float) übertragen. Nummer 5 in der Liste der Argumente ist die Zahl PI für einen Scan rund um das Objekt in alle möglichen Richtungen und last but not least das Argument zur Angabe der Intervalle in denen ein Scan durchgeführt werden soll. Wir haben hier 60 Sekunden angegeben, ein kleinerer Wert ist natürlich denkbar aber wir wollen ja keine Lags erzeugen.

Der Scanner ist nun also aktiv, wir müssen nun allerdings noch die Ergebnisse des Scanners auswerten. Hierzu gibt es zwei Ereignisse, die wir im Zustand aktiv einfügen. Das sind: sensor() und no_sensor()

Das eigentlich interessante ist no_sensor(), welches uns sagt, dass der Avatar nicht mehr gefunden wurde, allerdings brauchen wir auch ein sensor()-Ereignis, da sonst die Überprüfung abbricht.

Code:
	sensor(integer total_number)
	{
		// ...
	}
	
	no_sensor()
	{
		llInstantMessage(benutzerSchluessel,"Du wurdest abgemeldet!");
		state inaktiv;
	}

Das no_sensor()-Ereignis macht das gleiche, was das Anklicken des Objektes bewirkt, das Abmelden des angemeldeten Mitarbeiters.

Das Script in seiner Gesamtheit sind nun wie folgt aus:

Code:
string benutzerName;
key benutzerSchluessel;
integer einnahmen;
// Der Besitzeranteil wird in Prozent angegeben und muss zwischen 0 und 100 sein.
integer besitzerAnteil = 10;
// Die Reichweite des Scanners für den automatischen Logout
// 0 = ausgeschaltet
// 1 - 96 = Meter Reichweite
integer scannerReichweite = 30;

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

	state_entry()
	{
		if ((besitzerAnteil >= 0) && (besitzerAnteil <= 100))
			llRequestPermissions(llGetOwner(),PERMISSION_DEBIT);
	}

	run_time_permissions(integer permissions)
	{
		if (permissions & PERMISSION_DEBIT)
			state inaktiv;
	}
}

state inaktiv
{
	on_rez(integer start_param)
	{
		llResetScript();
	}

	state_entry()
	{
		llSetText("Trinkgelder
Derzeit kein Mitarbeiter angemeldet.",<1,1,1>,1);
	}

	touch_start(integer total_number)
	{
		if (llSameGroup(llDetectedKey(0)))
		{
			benutzerSchluessel = llDetectedKey(0);
			benutzerName = llKey2Name(benutzerSchluessel);
			einnahmen = 0;
			llInstantMessage(llDetectedKey(0),"Du wurdest angemeldet!");
			state aktiv;
		}
		else
			llInstantMessage(llDetectedKey(0),"Nur für Mitglieder der korrekten Gruppe!");
	}
}

state aktiv
{
	on_rez(integer start_param)
	{
		llResetScript();
	}

	state_entry()
	{
		llSetText("Trinkgelder
" + benutzerName + " angemeldet.
 Bisher wurden " + (string)einnahmen + " gespendet!",<1,1,1>,1);
		if (scannerReichweite > 0)
			llSensorRepeat(benutzerName,NULL_KEY,AGENT,(float)scannerReichweite,PI,(float)60);
	}

	sensor(integer total_number)
	{
		// ...
	}
	
	no_sensor()
	{
		llInstantMessage(benutzerSchluessel,"Du wurdest abgemeldet!");
		state inaktiv;
	}
	
	touch_start(integer total_number)
	{
		if ((llDetectedKey(0) == llGetOwner()) || (llDetectedKey(0) == benutzerSchluessel))
		{
			llInstantMessage(benutzerSchluessel,"Du wurdest abgemeldet!");
			state inaktiv;
		}
	}

	money(key giver, integer amount)
	{
		llSay(0,"Danke für das Trinkgeld, " + llKey2Name(giver) + "!");
		einnahmen += amount;
		llSetText("Trinkgelder
" + benutzerName + " angemeldet.
 Bisher wurden " + (string)einnahmen + " gespendet!",<1,1,1>,1);
		integer prozentFuerMitarbeiter = 100 - besitzerAnteil;
		integer geldFuerMitarbeiter = llCeil((float)amount * prozentFuerMitarbeiter / 100);
		if (geldFuerMitarbeiter > 0)
			llGiveMoney(benutzerSchluessel,geldFuerMitarbeiter);
	}
	
}
 

Users who are viewing this thread

Zurück
Oben Unten