changed(integer change)
The changed event is invoked when operations change certain properties of the prim/object. The change parameter is a bit field of one or more of the following values:
Constant | Value | Indicates | |
CHANGED_INVENTORY | 0x1 | changed object inventory (could include an item being added, removed, or renamed, or a notecard being edited and saved). | Details |
CHANGED_COLOR | 0x2 | changed object color or transparency | |
CHANGED_SHAPE | 0x4 | changed object shape (box to cylinder, for example), cut, hollow amount/shape, twist, top size, or shear | |
CHANGED_SCALE | 0x8 | changed object scale. | Details |
CHANGED_TEXTURE | 0x10 | changed object texture: offset, repeats, rotation, and reflection/bump maps (but not transparency — that’s CHANGED_COLOR). Also triggered when the glow status of the object is changed. | |
CHANGED_LINK | 0x20 | linking or delinking (also when an avatar sits on or unsits from the object). | Details |
CHANGED_ALLOWED_DROP | 0x40 | An item was dropped (into this object’s inventory) that was only allowed by the llAllowInventoryDrop function. This allows the object to identify items dropped by anyone who doesn’t have modify permissions on the object. | |
CHANGED_OWNER | 0x80 | The ownership of the object changed. This value is passed when an object is deeded to a group, when an object is purchased, or when a newly-purchased object is rezzed. | Details |
CHANGED_REGION | 0x100 | The object changed regions/sims. | Details |
CHANGED_TELEPORT | 0x200 | The object has been teleported. | Details |
CHANGED_REGION_START | 0x400 | The region this object is in has just come online. | |
CHANGED_MEDIA | 0x800 | The prim media has changed. |
These values are bitwise and can be ORed or ANDed together. Two flags can show up in the same event trigger; it has been seen when resizing an object with the “Stretch Textures” box unchecked so the texture scale (repeats) changes as the prim is resized. Also, the CHANGED_REGION and CHANGED_TELEPORT seem to come in tandem.With this in mind, always write “if (change & CHANGED_LINK)” rather than “if (change == CHANGED_LINK)” so that the contents of your event will be triggered in all cases. Likewise, using IF/ELSEs (as in “else if (change & CHANGED_SCALE)“) could theoretically result in only one of the triggers being counted. If you stick to with IF statements only, your changed event will need to perform a check for each IF statement, but it will catch all triggers in the very rare event of a simultaneous trigger. |
Notes:
- The change parameter describes only what type of change happened, not the difference between the original and changed versions of the object. If you need to determine exactly what changed, the script must store the relevant properties and compare the values from before and after the change.
- Prim/object position and rotation are not currently detected when changed but can be via a timer and llGetRot and llGetPos. Position changes can also be detected by using the moving_start and moving_end events.
- When an avatar sits on an object, they seem to become part of the linkset.
- A changed() event is raised with CHANGED_LINK as the parameter when an avatar sits on an object or un-sits. To find out what happened, use llAvatarOnSitTarget which returns the NULL_KEY if no avatar is sitting on the object, or the key of the avatar that is sitting at the sit target.
Example:
// When an avatar sits on the object containing this script, // the object will say "Get off!" and then force the avatar to stand up. default { state_entry() { llSitTarget(<0, 0, 0.1>, ZERO_ROTATION); // needed for llAvatarOnSitTarget to work } changed(integer change) { // something changed if (change & CHANGED_LINK) { // and it was a link change // llSleep(0.5); // llUnSit works better with this delay key av = llAvatarOnSitTarget(); if (av) { // somebody is sitting on me llSay(0, "Get off!"); // say in chat when person is remove from prim llUnSit(av); // unsit him } } } }
Details
Constant or Value: | changed() is triggered: | changed() is not triggered: |
CHANGED_INVENTORY |
|
|
CHANGED_SCALE |
|
|
CHANGED_LINK |
|
|
CHANGED_OWNER |
|
|
CHANGED_REGION |
|
|
CHANGED_TELEPORT |
|
(As of Second Life 1.16.0 (5) this is not accurate. Teleporting into no-script land will cause the event to trigger once you move onto script-enabled land, as though you had just teleported.) |
Aside from the above, the changed() event is also not triggered:
- when an object changes position. (Use moving_start() and moving_end().)
- when an object changes rotation.
- when changing the object’s name or description.
- when changing the object’s group. (not deeding the object to a group — that’ll trigger CHANGED_OWNER)
- when toggling “Share with group”, “Allow anyone to move”, “Allow anyone to copy”, setting the object “For Sale”, changing any of the sale settings, or setting “Next owner can:”.
- when toggling “Lock”, “Physics”, “Temporary” or “Phantom”.
- when setting prim’s material.
- when setting prim’s light properties.
- when using llSetText.
- when using llParticleSystem.
- in a worn attachment when the owner attaches or detaches it. (Use attach().)
- in a worn attachment when the wearer sits on something.
- when the object is selected/edited.
This script uses the changed event with a few other functions to see which notecard was updated, added, or deleted
//written by Ruthven Willenov, added to lslwiki 9/23/2009 list notes; list ids; integer note = INVENTORY_NOTECARD; default { state_entry() { integer tot = llGetInventoryNumber(note); integer i = 0; for(i;i < tot;++i) { string name = llGetInventoryName(note,i); key id = llGetInventoryKey(name); integer index = llListFindList((list)name,notes); if(index = -1 ) { notes += name; ids += id; llOwnerSay("Added notecard named: " + name); } } } changed(integer change) { if(change & CHANGED_INVENTORY) { integer tot = llGetInventoryNumber(note); integer i = 0; for(i;i < tot;++i) { string name = llGetInventoryName(note,i); key id = llGetInventoryKey(name); integer index = llListFindList(notes,(list)name); if(index == -1 ) { notes += name; ids += id; llOwnerSay("Added notecard named: " + name); } else if(index >= 0) { if(llList2Key(ids,index) != id) { ids = llDeleteSubList(ids,index,index); ids = llListInsertList(ids,(list)id,index); llOwnerSay(name + " was updated"); } } } integer len = llGetListLength(notes)-1; for(len; len >= 0; --len) { string name = llList2String(notes,len); integer type = llGetInventoryType(name); if(type == -1) { llOwnerSay(name + " was removed."); notes = llDeleteSubList(notes,len,len); ids = llDeleteSubList(ids,len,len); } } } } }
Q and A:
Q: How do I find out when my object has changed position or rotation?
A: You can’t, not by using changed(). The moving_start() and moving_end() event handlers are triggered when the object is moved. To find out when the object is rotated, your best option is to use a timer. (Unless, of course, your object is an attachment, in which case, it will never merely rotate, but will change position constantly, triggering moving_start() and moving_end().)
A: You can’t, not by using changed(). The moving_start() and moving_end() event handlers are triggered when the object is moved. To find out when the object is rotated, your best option is to use a timer. (Unless, of course, your object is an attachment, in which case, it will never merely rotate, but will change position constantly, triggering moving_start() and moving_end().)
Q: Regarding bitwise operations: it says above that scale and textures “have been seen” changing at the same time. Does that mean that both CHANGED_TEXTURE and CHANGED_SCALE will always be triggered while scaling a texture with an object?
A: Not necessarily, it just means that they occasionally will be triggered together. You can’t predict when this will happen, or even if it will. Make your code able to deal with all possibilities. Assumptions will often produce code that fails, with little indication as to why. As it says above, always write “if (change & CHANGED_LINK)” rather than “if (change == CHANGED_LINK)” so that the contents of your event will be triggered in all cases.
A: Not necessarily, it just means that they occasionally will be triggered together. You can’t predict when this will happen, or even if it will. Make your code able to deal with all possibilities. Assumptions will often produce code that fails, with little indication as to why. As it says above, always write “if (change & CHANGED_LINK)” rather than “if (change == CHANGED_LINK)” so that the contents of your event will be triggered in all cases.
Q: Is there a way to detect when a user buys an object?
A: Yes. Specifying CHANGED_OWNER for change will let you filter the results of changed(). See the details section for specifics.
To detect when a user pays an object, you want the money() event handler.
A: Yes. Specifying CHANGED_OWNER for change will let you filter the results of changed(). See the details section for specifics.
To detect when a user pays an object, you want the money() event handler.
Q: Is there a way to be notified via an event when a script is added or removed from the object’s inventory?
A1: No. Your only option is to use a timer to regularly compare the current inventory to a list of preexisting scripts.
A2: “The above answer is wrong. Use CHANGED_INVENTORY to compare the current inventory to a list of preexisting scripts, not a timer.”
A3: “Both answers are correct. You can detect when something gets added with a changed event, but NOT when it gets removed. It needs a timer to detect removals.”
A1: No. Your only option is to use a timer to regularly compare the current inventory to a list of preexisting scripts.
A2: “The above answer is wrong. Use CHANGED_INVENTORY to compare the current inventory to a list of preexisting scripts, not a timer.”
A3: “Both answers are correct. You can detect when something gets added with a changed event, but NOT when it gets removed. It needs a timer to detect removals.”
Q: Can I use the detection functions (llDetected*) with changed()?
A: No, they only work with touches, collisons and sensors.
A: No, they only work with touches, collisons and sensors.
Q: Can I find out who (or what) caused the object to change?
A: Not intrinsically, but this can be programmed. One can compare parameters/states/keys/features of an object before and after the changed event to determine what caused the change. For example, to find out what changed the inventory of a prim, first on initialization (state_entry), get the UUID keys of the existing contents using llGetInventoryKey() and store to a variable. When an item is edited and saved it is assigned a new key and the changed event occurs. Compare the new key of the item using llGetInventoryKey() to the previously stored key. If the key’s match the item is unchanged. If the keys do not match we know what item changed the prim’s inventory. For CHANGED_OWNER, compare new owner key to previous owner key. For CHANGED_SCALE compare the previous scale to the new scale, etc. One can also determine what event triggered the change event by a debug llSay/llOwnerSay message in the event. Example, a touch causes a scale change.
A: Not intrinsically, but this can be programmed. One can compare parameters/states/keys/features of an object before and after the changed event to determine what caused the change. For example, to find out what changed the inventory of a prim, first on initialization (state_entry), get the UUID keys of the existing contents using llGetInventoryKey() and store to a variable. When an item is edited and saved it is assigned a new key and the changed event occurs. Compare the new key of the item using llGetInventoryKey() to the previously stored key. If the key’s match the item is unchanged. If the keys do not match we know what item changed the prim’s inventory. For CHANGED_OWNER, compare new owner key to previous owner key. For CHANGED_SCALE compare the previous scale to the new scale, etc. One can also determine what event triggered the change event by a debug llSay/llOwnerSay message in the event. Example, a touch causes a scale change.