User:SuzannaLinn Resident/ScriptingClasses/Tipjars

From Second Life Wiki
< User:SuzannaLinn Resident‎ | ScriptingClasses
Revision as of 11:10, 5 November 2024 by SuzannaLinn Resident (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Tipjars

Monday class

Tipjar prim

-> Object Box for class on tipjars

First of all I want to thank Ayumi Cassini for her "ultimate guide to prim twisting" posted in her blog, here:

The twisting for the object that we will be using as a tipjar come from there. Take a look at her blog, but after the class, please :)


Make an object to behave as a tipjar

Now your tipjar has no scripts, and you can't right click and pay to it.

We are going to make it "payable".

Look at your Class Materials folder and copy the "Script 1 Tipjar minimal" into the tipjar contents

Now try to pay to it, and it will behave as a tipjar, showing default amounts and accepting payments. Pay it 1 linden and that will come to you. You are paying to yourself.

-> Script 1 Tipjar minimal"

The script is... rather empty.

There is only an empty event "money". And this is enough to make an object to behave as a tipjar.

All the management of the payment is done by SL.

SL opens the window with the suggested amounts. SL accepts the lindens from the payer. SL sends the lindens to the owner of the object. Always all the lindens, always to the owner of the object.

And there is no way to change this process in our script.

Of course this is not a nice tipjar, from now on we will see how to improve it.

The several scripts that we will see all have the same code as the previous one with some more lines added.


Add a text and a thanks message

Delete the script in your tipjar.

Look at your Class Materials folder and copy the "Script 2 Tipjar text and messages" into the tipjar contents.

-> Script 2 Tipjar text and messages

First we have a list of constants with color codes to use in the floating text of the tipjar, in the same way that we saw in the previous class on floating texts.

Let's move to line 18 in the script.

The payment options dialog box that SL shows to us is configurable. We can change the four default values, and also hide the option to write another amount.

We use two variables payButtons (for the default values) and payPrice (to show or hide the text option).

payButtons is a variable of type "list".

In our case, payButtons has to be a list of four values. And payPrice will be set to the LSL constant PAY_DEFAULT, which shows the text box to type a different amount.

This is the usual way for a tipjar. Commented on lines 18-19 there are the options for a vendor. It will show the payment dialog box with only one button (with 150 in this example) and no textbox. It will only allow you to pay 150. Try it later.

Now let's look at line 36 in the event state_entry.

llSetPayPrice() is the function that sets our parameters for the dialog box.

llSetClickAction() sets the default action when an object is clicked. In the previous script we had to right-click and choose "pay".

Which is too much work, we want people to tip us comfortably. Setting the default action to payment they will just need to click.

This is all about configuring the payment. Now we will add a floating text to the tipjar, so people know that it's a tipjar.

Still in the event state_entry, we set the variable myName to... your name. Since we will be using it in several places, it's better to have it in a variable. Less typing and for the script is faster to access variables than LSL functions.

We set the variable lastAmount (it will have the last tip amount) and tipjarAmount (with the total of tips) to 0, to be ready to count tips.

And we call our function showText() to show the tipjar floating text. We will call it from several places, so it's better to make a function for it. It also makes the script easier to read and the function easier to reuse in other scripts.

Let's take a look at showText() at line 24.

We add all the info to the variable "text", using "\n" to make a new line.

The operator += is a shorter way to write an addition to a variable, instead of writing:

  • text = text + "\n";

we write:

  • text += "\n";

At line 30 with add 3 new lines with one space. This makes the floating text to be displayed in a higher position above the tipjar. Otherwise it would show partially on the tipjar.

And we write the floating text with llSetText(), using the text, color white, and the third parameter is the transparency, 1 means fully opaque, so as much visible as possible, as we saw in the previous class.

Now let's move to the event "money" at line 43.

The event has two parameters.

key id is the UUID of the giver and integer amount is the quantity given.

We give thanks to the giver getting their name from the "id" with llGetDisplayName().

We update the counting variables, and we call showText() to show the updated text with the new values.

Try giving to your tipjar to see the thanks message and the floating text updating. If you are feeling generous you can give to the other students tipjars too :)


Synchronize several tipjars

Delete the script in your tipjar.

Look at your Class Materials folder and copy the "Script 3 Tipjar in sync" into the tipjar contents.

-> Script 3 Tipjar in sync

Now we will synchronize all our tipjars to show a total amount of tips given to them all.

To synchronize our tipjars we will make them to chat with one another.

We need to open a private channel of comunication. We will type our new text in this channel and our script will be listening in the same channel.

We can choose any number for our channel, up to 9 digits. In this script we will use channel 15.

Let's see how we will make our script to listen.

The function is llListen(). It has 4 parameters:

  • the channel we want to listen to (number 15, in our example).
  • the user name of the person we want to listen to.
  • the UUID of the person, we will use this parameter or the user name parameter, not both.
  • the message, if we are listening for a specific message.

We have to choose a channel to listen, the other parameters are optional, and we will use them to filter the message.

In this case we will listen to channel 15, and we want to listen to all the tipjars so we will not use the other parameters:

  • llListen( 15, "", "", "" );

And what happens when the script receive a message to the channel 15? It triggers the event listen, which has the same parameters than the llListen() function.

In the event listen is where we will update the variable totalAmount (with all the tips from all the tipjars) and show the updated floating text.

Let's look at our script to see what we have added to the previous script.

At the start, in line 18, there is:

  • integer CHANNEL_TIPJAR=15;

We could have used the number 15 in the listen function and event, but it's better to use a constant with a nice name or it would get very confusing if we listen to several channels.

In the event state_entry, at line 72, we have the function llListen().

In the event money, at line 81, we say on CHANNEL_TIPJAR the amount that our tipjar has been tipped.

We use llRegionSay() instead of llSay(), because it sends the message to all the region while llSay() sends the message only to 20 meters from the object.

Using llSay(), if the tipjars are not near, they will lose messages.

And at the end of the script, at line 84, we have added the event listen.

Inside the event listen we check that the channel is the our CHANNEL_TIPJAR. No need to do this here. We are only listening to a channel and we will not receive messages from any other channel. But it's better to do it, in case that we add more channels to our script in the future.

We update the totalAmount and we show the floating text.

We also update the totalAmount in the event money, at line 79, together with the other amounts.

Why? because objects doesn't receive the messages that they say. If we only listen to messages, the total amount will miss the tips to our tipjar.

In this script we are also making an improvement to our thanks message, that was:

  • llSay(0,"Thank you very much, "+llGetDisplayName(id)+", for your splendorous tip!");

It had two problems: perhaps we want the thanks message to be private, and if the giver is at more than 20 meters of the tipjar, the thanks message will be lost.

Now we have replaced it, in line 82, with the call to the function sayThanks(id), sending the UUID of the tipper as the function parameter.

We have done it a bit over-complicated, because we like to learn a lot :)

First, we have added the parameter PUBLIC_THANKS, if it's TRUE thanks will be public, if FALSE thanks will be private. You can change it to FALSE later to see it.

TRUE and FALSE are LSL constants. They are integers, TRUE is 1, FALSE is 0. We can use it in this way:

	if ( PUBLIC_THANKS ) {
		// execute this when PUBLIC_THANKS is different to 0
	} else {
		// execute this when PUBLIC_THANKS is 0
	}

Let's go to line 39 to look at the function.

The public option is the complicated one. We want to be sure that the giver receives the thanks message.

We will use llSay() if the giver is in 20 meters, if not we will use llShout() if the giver is in 100 meters.

No way to send public messages to more than 100 meters, so we will use llRegionSayTo(), to send a message to a person anywhere in the sim. This is a private message, but there isn't any other way. And since we want it public, we will also shout it with llShout().

By the way, I haven't tried to tip a tipjar from more than 100 meters away... I don't even know if it is possible.

But as scripters, we must make our scripts ready for anything :)

Now let's find out how to know the distance of the tipper to the tipjar.

To get the position of the tipper we will use llGetObjectDetails(). It has two parameters: the UUID of the person (or it could be an object) and a list with the information that we want the function to return.

There are lots of options, here we will use OBJECT_POS which gives us the position of the person or the object. The function expects a list, so, even if there is only one element, we must use a list in the parameter. We make a list with one element just adding [ and ]:

  • [OBJECT_POS].

llGetObjectDetails() also returns a list. If we ask for several informations, not only the position, it will return all the information in the list. In our script we get a list of one element, the person position, as return.

But we want a vector to assign to tipperPos. We will use llList2Vector() to get one element of the list to a vector, the element that we choose in the second parameter, in this case "0". List indexes start counting by 0, and a list of one element only has the element 0.

There are also functions llList2String(), llList2Integer(), llList2Float(), etc, to use depending on the type of our list elements.

With llGetPos() we get a vector with the position of the tipjar into our variable tipjarPos.

And to find the distance between tipperPos and tipjarPos we use the function llVecDist() that returns the distance in meters.

It always returns a positive value. If the distance from A to B is 10, the distance from B to A is also 10.

And the private option is easy: just use llRegionSayTo().

Try giving to your tipjar and look at the other tipjars to see if all of them updates the total tips.


Share tips

Delete the script in your tipjar.

Look at your Class Materials folder and open the "Script 4 Tipjar share tips" to look at it, don't copy it to the tipjar yet.

-> Script 4 Tipjar share tips

I said when we were looking at the first script that the tips always go to the owner of the object, at that it's not possible to change it. So how can we share the tips?

There is a function to give money. We will, as owners of the object, receive all the full tips, and we will give part of the tips to someone else.

The function is llGiveMoney(). It has two parameters: the UUID of the person whom we want to give the money, and the amount to give. Obviously the amount to give has to be in our lindens acount.

This is a dangerous function, we could become poor using it. So it need to be given a permission by the object owner.

Let's look at how to give the permission. It includes a function and an event. We also will use a variable to know if the permission has been granted or not.

The function, at line 67 in the state_entry event , is llRequestPermissions(). It has two parameters: our UUID that we get with llGetOwner() and the kind of permission that we are asking for. The LSL constant for the permission to give money is PERMISSION_DEBIT.

The event, at line 78, is run_time_permissions. It has the parameter perm which is used as a list of bits. We will use the operator & (bitwise AND) to check if the PERMISSION_DEBIT bit is at 1, meaning that the permission has been given. If so we will set our variable debitPerms to TRUE.

I'm always placing constants at the start of the script, and making functions as often as possible, and telling you to do it. But being llGiveMoney() a dangerous function I will tell you to place everything together inside the event money.

Let's look at the event money, at line 83.

SHARE_WITH_ID will be the UUID of the person that you want to share the tip with. Copy here the UUID of your alt, your best friend, or yourself to test the script. In this script, if you share with yourself, with a tip of, for instance, 10 lindens, you will get 10 lindens from yourself, and then 5 lindens from yourself.

SHARE_PER_CENT is the percentatge of sharing, 50% in this example.

With this parameters we calculate shareAmount rounding it with llRound().

llRound() rounds 0.5 to 1 so with a tip of 25 lindens, we will send 13 and keep 12.

Then it checks if we have added the UUID to share with.

In line 90 we are checking that the amount that we are going to share is less or equal than the amount that we have received, with:

  • if ( ShareAmount <= amount )

if ( ShareAmount <= amount ) is unnecessary here, it will always be true. We have added it to our script as a good scripting practice.

In case that a very complicated function or in case of a dangerous one, it's good to assert that the values are in the expected ranges.

In this example we make sure not to send more lindens that we have been tipped.

Remember, the function llGiveMoney() is not related to the event money and neither to tipjars. It doesn't try to know our intentions. It will happily, without any complain, give all our lindens to any avatar in all sl, including the ones being away for years.

Look at your Class Materials folder and copy the "Script 4 Tipjar share tips" into the tipjar contents. As soon as the script is there, it will ask you for the permission to give money.

Now give some tips to your tipjar and look at the transactions to see if it shares well.


Answers to questions

Could we use more than 4 pay buttons in llSetPayPrice()?

  • No, it is limited to 4 buttons by the dialog window that SL opens when we pay.
  • We can't change the format of the dialog window, only provide the parameters with the script.

Is PAY_HIDE to replace the other values if we don't have 4?

  • Yes, if we want to show less than 4 buttons, we need to use PAY_HIDE in the list for the empty options.

Can we change the default action manually, without using llSetClickAction()?

  • Yes ,we can change the click action with the object editor, in the "general" tab, in the drop down list "click to".

Is there a way to update the amount into tipjars across multiple regions?

  • There is a way, but more complicated, we will see it in a future class.

If we have the tipjars grouped in several sets, can we just use different channels?

  • Yes, to have different groups of tipjars showing totals of tips we can use a different channel for each group of tipjars.
  • Another way is setting the tipjar description, in the edit window, to the name of the group, and the scipt only acumulates amounts from tipjars with the same group in the description.

Could we save information, like the name of the tipper and the amount, to a file?

  • There is no way to write in a file. The only ways to export information are using http to connect to an external web, or sending an email.
  • We can save the information in the Linkset Data, that is more permanent than the script memory. We will study linkset data in a future class soon.

Does llRegionSayTo() gets to an avatar anywhere in the region?

  • Yes, the range of llRegionSayTo() is all the region.

Do messages send with llRegionSayTo() show in the public channel? Or do we have to use the event listen?

  • llRegionSayTo() sends the message only to the avatar which uuid we use in the first parameter.
  • It's not public but it appears in then local chat window, if we use channel 0 as the second parameter.
  • We can also use llRegionSayTo() to send a message to an object, using another channel, and the other object has to listen to that channel.

We could manipulate it like '/15 100'. Can we avoid it somehow?

  • Yes, we will study it on Wednesday.

What is "perm"?

  • "perm" is the parameter that we receive in the event run_time_permissions.
    • When the script is reset, it opens a dialog window requesting for the permission (to give money in this script).
    • It's a dialog window managed by SL called when we use llRequestPermissions(), as we do in state_entry.
    • When we accept or deny the permission in the dialog, it triggers the event run_time_permissions, and the parameter "perm" has the information about the permissions accepted.
  • "perm" is a bit mask with all the permissions, each bit is a different permission.
  • We check the bit of the permission to give money with the constant PERMISSION_DEBIT.

Does the tips to our tipjar go to our SL account as soon as they are tipped? Can we differentiate tipjar money from our total funds?

  • The tips go automatically to our lindens total amount. We must be very careful on what we give.
  • The only way to differentiate besides keeping our own records would be to make payments to an alt or have the alt own the tipjar to receive the payments directly.
  • Be very careful with script scripts that give money. Perhaps use an alt with a limited budget as payer so you don't accidentally bankrupt yourself if things go wrong.
  • A security point if you're using this as a vendor where you want a specific amount:
    • While the payment pop-up can be set to pay only a particular amount, there is nothing stopping someone from paying your object a different amount, including L$0 to trigger the money event.
    • So for vendors, always always check to be sure you got the right amount. Best practice is to return wrong amounts with an appropriate notice.
    • We will study an example of this on Wednesday.