Jump to content

LepreKhaun

Resident
  • Posts

    1,384
  • Joined

  • Last visited

Everything posted by LepreKhaun

  1. As Charliedru rightly points out, a lot of "standard" functions (such as getAllKeys, sortKeys, sortArraybyValue, addElement and deleteElement) are missing. So we have to roll our own. Fortunately, that can be done with the get and set functions that we do have, it just requires some thought on how the JSON object is constructed (http://json.org/ and http://wiki.secondlife.com/wiki/Json_usage_in_LSL ) and a familiarity with the LSL list functions (http://wiki.secondlife.com/wiki/List ). And at least, once a getAllKeys function has been worked out, it can be reused in other code. So, the short geeky answer: One deletes a member/element, which may be a leaf node or an entire branch, the same as you would add one to an existing JSON object- by "getting" its parent node, converting that to a list with llJson2List, manipulating that list to delete or add what you wish, converting the new list back to a JSON object with llList2Json and then "setting" its parent node with that result. The main thing here is that the parent node may be either a JSON_ARRAY or a JSON_OBJECT and the list manipulation you do differs accordingly. If it is a JSON_ARRAY, it's pretty straight forward, you'll just remove the list element from the list at the specified index. However the JSON_OBJECT is a bit trickier because you are now dealing with a 2-strided list. First you have to find the index of the KEY within the list and then remove both that AND the next higher index (which is the associated VALUE of the pair) as well. Be very careful doing this! Decide whether you are deleting a leaf node or pruning an entire branch from your data structure tree (http://en.wikipedia.org/wiki/Tree_(data_structure) ) and ensure your code will run correctly after the deletion. Once you do the deletion, it's gone for good and you don't want your code to later step off a cliff by going into the JSON object and failing to find what it expected to be there. // Function: string deleteJsonElement( string jsonSource, list pathtoElement );// Version 1.0 by LepreKhaun, 6/21/2013. Free to copy, modify and use as one wishes with this comment included.// This function takes a JSON object and returns a copy of it with a specified element removed from it.// jsonSource is the JSON object to remove an element from.// pathToElement is a List containing the transversal path to the element// (as defined as "specifiers" in http://wiki.secondlife.com/wiki/Json_usage_in_LSL )// AND ending with the value to remove.// If the parent of the element is a JSON_ARRAY, the value to be removed must be an integer INDEX // If the parent of the element is a JSON_OBJECT, the value to be removed must be a string KEY//// This code is not optimized nor does it contain any error checking!!! // Add whatever you feel is needed to ensure correct operation of your script.string deleteJsonElement( string jsonSource, list pathtoElement ) { integer placeinlist; // Obtain the JSON object of the parent of the element string jsonParent = llJsonGetValue( jsonSource, llList2List( pathtoElement, 0, -2 ) ); // Convert it to a list list listParent = llJson2List( jsonParent ); // Do test if (llJsonValueType( jsonParent, [] ) == JSON_ARRAY) { placeinlist = llList2Integer( pathtoElement, -1 ); listParent = llDeleteSubList( listParent, placeinlist, placeinlist ); // convert to a JSON object jsonParent = llList2Json( JSON_ARRAY, listParent ); } else // We are dealing with a JSON_OBJECT { placeinlist = llListFindList( listParent, llList2List( pathtoElement, -1, -1 ) ); listParent = llDeleteSubList( listParent, placeinlist, placeinlist + 1 ); // convert to a JSON object jsonParent = llList2Json( JSON_OBJECT, listParent ); } // Insert our results and return return llJsonSetValue( jsonSource, llList2List( pathtoElement, 0, -2 ), jsonParent ); } It is left as an exercise for the reader to construct the matching function, addJsonElement. Hope that helps! [Edited to correct an error in camelCase in code. Sorry!]
  2. Charliedru Lannock wrote: ... I am curious exactly as to how fast llCastRay replenished its pool. The wiki states a frame, but I am unfamiliar with a time value with respect to a frame. Would 200-1000 rays every 2-3 seconds be too much for the current and/or possible future limitations of llCastRay? Any help would be appreciated, thanks. CDru A "frame" in this context* is referencing how the sim server updates the sim. A frame happens, ideally, 45 times each second though it tends to vary a small amount each second. Each frame entails a LOT of activity which is done in frame subdivisions referred to as "slices", which are allocated to all active elements (avatars in the region, active tasks, falling objects etc). So, 2-3 seconds would be 90-135 server frames and 200-1000 rays equates to app. 2-8 rays being attempted by your task during each slice of its processing time. Whether that is too much or reasonable would have to be determined by you for your specific case. * The viewer also has its own "frame rate", which is dependent on many factors outside of SL, including the internet connection at the time, video card and processing power of the client's computer, and (possibly) the sun position in Delhi at the moment. .
  3. The only way someone can realize they are banned from a property is is when they try to access it or, of course, if they are on the property at the time they are banned (in which case they are ejected and told that they were ejected because of the ban). And, yes, they will still be able to see your profile if they try. The ban itself becomes an aspect of the land and is not directly tied to the person that banned the other. In fact, when someone is informed they can't enter a property because they have been banned from it, they are not told who it was that banned them (which may be one of several people in the case of group owned land or a private estate) but simply will get the error message "You are banned from the region."
  4. This seems to be a rare problem and very perplexing when it happens. One thing that has worked in the past is installing a different viewer and attempting to log in with that. For some reson, one successful log in will clear up the problem and you can use your regular viewer from that point on.
  5. When posting an event for a mature or Adult sim, the only way I know of to delete or edit it is to have saved the link from the confirmation page, and then use that link to get back to the posting. If you failed to save it, I don't believe there is anything to be done, sorry, I hope I am corrected if this is wrong.
  6. Yes, the old Landmarks should still work properly after a name change. SLURLs, however, are a different matter...
  7. I'm pleased you like my implementation of filtering with a single flag, I did come up with it after considering your critique of my original coding and find it workable enough to change the few scripts I've written with both a savings in code size as well as an improvement in readability. And no loss in scalability to boot!
  8. Always a pleasure. Good luck and remember to pick up all those hats later.
  9. You're missing a closing curly-brace after the line "state WaitForIt;" .You need one to close the "if" code block, one to close the "collision_start" block as well as one to close the "default" state block.
  10. Though I see some validity to Rolig's critique of my original coding, I find too many faults with her proposed solution of filtering by the response. In the (not so unlikely) event that OP may decide to also be able to change the object's description, her method will fail to scale without adding a variable to determine which of two textBoxes is triggering the listen event. It also precludes the use of "More" to continue a menu level when there are more than 10 choices to offer as well as (say) mainMenu and subMenu1A both having a button named "Red". All that on top of careless consideration of the llTexBox response. So, back to the drawing board... // global variablesinteger menuChannel;integer menuHandle;string menuSelected = ""; // We'll use this variable to keep track of the last offered menudefault{ state_entry() { menuChannel = -1* (integer)(llFrand(2000000000.0) + 1); // Get a random negative integer } touch_start(integer num_detected) { // Prepare to open the mainMenu // llListenRemove (menuHandle); errr, let's not do this unless anyone else nearby is welcome to disrupt the listen... // To ensure we're dealing with the owner from the start, .... if (llDetectedKey(0) == llGetOwner()) { menuSelected = "mainMenu"; menuHandle = llListen (menuChannel, "","", ""); // Get the new handle llSetTimerEvent (120.0); // Present the mainMenu widget using llDialog with... llDialog ( llGetOwner (),"This is a dialog prompt message",["Button 1", "Button 2", "Name Object"], menuChannel); } } listen (integer channel, string name, key id, string message) { llSetTimerEvent (0.0); // Clear the timer event immediately // Now filter by menuSelected to see what has responded if (menuSelected == "mainMenu") { // Doing this where I do through the program will alert me... // ...anytime I might add a menu later without first setting this variable menuSelected = ""; // Defensive programming // Also, it is good practice to always close the listen promptly, as soon as it is no longer needed... // ... Failing to do it here leaves open the possibility of failing to close it while in the "Button 1" and "Button 2" code blocks llListenRemove (menuHandle); if (message == "Name Object") { menuSelected = "textBox1"; // Reopen the handle now menuHandle = llListen (menuChannel, "","", ""); llSetTimerEvent (120.0); llTextBox (llGetOwner (), "Please enter New Name for Object", menuChannel); } else if (message == "Button 1") { // Code block to handle Button 1 } else if (message == "Button 2") { // Code block to handle Button 2 } else { // I'd use a final else here to alert me of unaccounted for situations // If one gets into that practice, hours of head scratching can be avoided llOwnerSay ("Unhandled response to Main Menu variable:" + message); } } else if (menuSelected = "texBox1") { llListenRemove (menuHandle); // Close that listen! menuSelected = ""; // Defensive programming message = llStringTrim (message , STRING_TRIM); // Strip possible new line llSetObjectName (message); // Change Object Name llOwnerSay ("Object has been renamed to " + message); // Tell the owner of the change } else { llOwnerSay ("Unhandled response to: " + message + "due to menuSelected variable being: " + menuSelected); } } timer() { llSetTimerEvent (0.0); // Clear the timer event first thing llListenRemove (menuHandle); menuSelected = ""; // Defensive programming // Good practice is to now let the user know that the menu is no longer functional llOwnerSay ("MENU TIMEOUT"); }} Now the OP has a scalable solution using a simple, straight forward string comparison (don't get me started on unneeded lists, static or otherwise, and the hidden overhead inherent in them).
  11. Rolig Loon wrote: Since you are never using a dialog and a textbox at the same time, you can use one channel safely to serve both of them. There's also no need to close the listener unless (a) the textbox response has been received or (b) the timer has run out. You can write the same script more simply this way .... // global variablesinteger MainChannel;integer MainHandle;default{ state_entry() { //You only need to create a channel number once, so do it in the state_entry event //Be sure to add 1 to the random number so that you never end up with the channel number = zero MainChannel = -1* (integer)(llFrand(2000000000.0) + 1); // Get a random negative integer } touch_start(integer num_detected) { // Prepare to open the mainMenu llListenRemove (MainHandle); // To ensure we're dealing with the owner from the start, .... if (llDetectedKey(0) == llGetOwner()) { MainHandle = llListen (MainChannel, "","", ""); // Get the new handle llSetTimerEvent (10.0); // Ten seconds is more than enough time to click a dialog button // Present the mainMenu widget using llDialog with... llDialog ( llGetOwner (),"This is a dialog prompt message",["Button 1", "Button 2", "Name Object"], MainChannel); } } listen (integer channel, string name, key id, string message) { llSetTimerEvent (0.0); // Clear the timer event immediately // Now filter by message to see what has responded if (~llListFindList(["Button 1", "Button 2", "Name Object"],[message])) //If this is one of the dialog button labels { if (message == "Name Object") { llSetTimerEvent (30.0); // Thirty seconds is plenty of time to type a text box response llTextBox (llGetOwner (), "Please enter New Name for Object", MainChannel); } else { //Deal with the other dialog responses } } else // Any other response is a text box response, by default { llListenRemove (MainHandle); // Close that listen! message = llStringTrim (message , STRING_TRIM); // Strip possible new line llSetObjectName (message); // Change Object Name llOwnerSay ("Object has been renamed to " + message); // Tell the owner of the change } } timer() { llSetTimerEvent (0.0); // Clear the timer event first thing llListenRemove (MainHandle); // No harm in closing a handle if it isn't open... // Good practice is to now let the user know that the menu is no longer functional llOwnerSay ("MENU TIMEOUT"); }} Reminding everyone, once again, of Murphy's Law; do you see what happens if the user would decide to name their object "Button 1"? This flaw only increases as more dialogs (each of which can have up to 12 buttons) are added. Carelessly handled user input can play havoc in a program and failing to code for the worse case scenario will eventually leadto heartbreak...
  12. Yes, it is good to disagree this way at times in that it does give the reader the option of seeing differing styles/solutions to the same problem, giving them the option of choice or, perhaps, inspiring them to come up with their own. As long as the main point isn't lost in the shuffle, which is the need for listens to be closed as soon as possible when used.
  13. We seem to differ in programming styles. I tend towards readability and ease of maintanence (which includes scaling the program up at some future date) over saving a few bytes of program size. To each their own, I guess.
  14. Though your example will work for this specific case, it won't scale properly. Using a discrete channel and handle for each menu dialog enables one to handle mainMenu, subMenu1, subMenu2, subMenu1A, subMenu1B etc as needed.
  15. You're not only correct in that assumption, there is another complicating factor in using different prims to store animations: Each prim can only access its own inventory. That would entail the script in each needing to communicate with the main HUD program with what it has, as well as managing updates on their individual CHANGED_INVENTORY events. Much better, IMO, is the original suggestion of prefixing the names with 'M' or 'F' (and possibly 'U' for unisex?) and sorting the animations into lists. That hierarchy could be further extended by using the second letter to designate sitting, standing, walking, dancing etc.
  16. Trevor Scarpulla wrote: Is there a way to Name a Prim through a local Channel Message from the Owner? My script is laid out like This: I have a Dialog Box that prompts a message "Name Object". Then is Calls the prescripted prim in Question to listen. if(message == "Name Object") { llOwnerSay("Please List name in Local Chat"); llListen(0, "","",""); llSetObjectName(-Name Here-); But I cant Figure out how to set it to listen to the owners Message. I can trigger the llSetObjectName command through the command from the dialog box but I can't figure out how to get it to listen to the Selected channel so the owner can name the object though the chat window. Any Ideas? I know I'm skipping a step but I can figure out how to make it work. As Xiija pointed out, you should study the wiki entry for llListen. While doing so, keep in mind that the listen event is a necessary evil, like fire. Though it has its uses, one can be badly burned if it isn't used properly. There are two distinct dangers here- the overhead involved (which increases with each listen opened, especially when one is listening on Channel 0, open chat) and the chance of channel conflict (which increases the longer a listen is open). To avoid both, it is Best Practice to always close a listen as soon as possible. And to close it one must have its "handle", the integer returned from the llListen call. This is done with "llListenRemove (listen_handle);" as soon as the script no longer is needing that listen anymore. Closing a listen ASAP also has another consideration when using the menu interface (as with llDialog or llTextBox)- the user may choose to Ignore it or close it with nothing entered. There is no way for your script to realize this when that happens, so a timer must be used that will close the listen if no response is received after (say) a minute or two. For readability and ease of maintanence, I'd recommend using seperate global variables for each listen the script may use. In other words, each listen should have a variable used for it's channel assignment as well as a variable for its listen handle. The channel variables are then used to filter the response within the listen event to determine what it is listening to (mainMenu, subMenu1, subMenu2, ..., textBox1, textBox2, ..., localChat etc). So, the necessary code entailed is: // global variablesinteger mainMenuChannel;integer mainMenuHandle;integer textBoxChannel;integer textBoxHandle;// and so on, as neededdefault{ // Assuming you're presenting the menu dialog within a touch event touch_start(integer num_detected) { // To ensure we're dealing with the owner from the start if (llDetectedOwner (0) == llGetOwner ()) { // Prepare to open the mainMenu mainMenuChannel = (integer)(llFrand (-2000000000.0) - 2000000000.0); // Get a random negative integer llListenRemove (mainMenuHandle); // Definitely overkill, but just to be sure! mainMenuHandle = llListen (mainMenuChannel, "", "", ""); // Get the new handle llSetTimerEvent (120); // Two minutes allowed for response, adjust as you wish // Present the mainMenu widget using llDialog with... llDialog ( llGetOwner (), string message, list buttons, // one of which will be "Name Object" mainMenuChannel ); } } listen (integer channel, string name, key id, string message) { llSetTimerEvent (0); // Clear the timer event immediately // Now filter by channel to see what has responded if (channel == mainMenuChannel) { llListenRemove(mainMenuHandle); // Close the listen! // Code to handle the Main Menu response goes here // which may include the following... if (message == "Name Object") { // Prepare to open the textBox widget textBoxChannel = (integer)(llFrand (-2000000000.0) - 2000000000.0); // The next line may seem like overkill to someone unaware of Murphy's Law. mainMenuChannel = 1; // Avoid internal channel conflict llListenRemove (textBoxHandle); // Definitely overkill, but just to be sure! textBoxHandle = llListen (textBoxChannel, "", "", ""); // Get the new handle llSetTimerEvent (120); // Two minutes allowed for response, adjust as you wish // NOW, present the textBox widget! llTextBox (llGetOwner (), "Please enter New Name for Object", textBoxChannel); } } else if (channel == textBoxChannel) { llListenRemove (textBoxHandle); // Close that listen! message = llStringTrim (message , STRING_TRIM); // Strip possible new line llSetObjectName (message); // Change Object Name llOwnerSay ("Object has been renamed to " + message); // Tell the owner of the change } } timer { llSetTimerEvent (0); // Clear the timer event first thing llListenRemove (mainMenuHandle); // No harm in closing a handle if it isn't open... llListenRemove (textBoxHandle); // ... so we close all that may be open! // and so on, as needed // Good practice is to now let the user know that the menu is no longer functional llOwnerSay ("MENU TIMEOUT"); }}
  17. Miranda Umino wrote: What is the interest to talk on the local chat to set the name of object ? you may use llTextBox : ( http://wiki.secondlife.com/wiki/LlTextBox) Every viewer has implemented it for a long time listen(integer channel, string name, key id, string message) { message = llStringTrim(message , STRING_TRIM); if(message == "Name Object") { llOwnerSay("Please List name in Local Chat"); llTextBox(channel, id, "Please list name in this text box") } else if ( message != "") { llSetObjectName(message); llListenRemove(handleListener); } } If you had at least checked that link you gave, you would have found "Function: llTextBox( key avatar, string message, integer channel );". Also, the way you've written it, if any user or task chats anything else but "Name Object" on that channel, the object will be silently renamed and the listen removed, probably not a good thing.
  18. Pamela Galli wrote: Oh the reason that worked -- the .obj was separated into separate meshes by material, although it was exported from one mesh. The bugged face with two materials was not separated into two meshes, tho. That was a good clue to what is going wrong. It's not that the "bugged face" has two materials, it's that your material there is either being mapped to two differing UV images or you have overlapping UV islands from different materials within that area. This is completely allowable and is "a good thing" within Blender (allowing, for instance, smooth blending from facial features into the hairline) but can lead to problems in SL, where we deal with material "faces" and expect unique UV assignment of each.. To rectify/avoid that you need to unwrap everything again, making and saving a NEW UV texture for each individual material. Every material should have its own UV mapping with no other materials involved in it. See http://wiki.blender.org/index.php/Doc:2.6/Manual/Textures/Mapping/UV/Layout_Management for details on how to do this. I'll also IM you with some personal specifics regarding this.
  19. It may be locked. While in Edit Mode (that is, while your edit window is already open), try right clicking the box to force its selection and then unlock it if it is.
  20. LepreKhaun

    SOLD OUT!

    All lots have been sold.
  21. Wandering Soulstar wrote: ... LepreKhaun - Question, does doing your suggestion increase the LI? Though adding material faces to your model does may* have a small impact on Download weight, I doubt it would be enough to affect your final Land Impact. One would have a model whose LI was not only being driven by Download weight (as opposed to Physics or Server weights) but as well as be on a cusp with the other Download factors (size and geometric complexity) for material faces to tip the scale. And, unless your door is extremely complex or overly large in its construction, it should be having its Land Impact set by the Server weight and should show as 0.5. *ETA there are some models (composed of disjoint planes) that adding material faces will not affect download weight whatsoever since the vertex count would remain the same.
  22. Wandering Soulstar wrote: Thanks for the answers .. guess I should have checked in code first. Sadly though this means that the functionality that I had before in the code, before I turned the door to Mesh will not longer work. Before, depending on the side if the door you touched it would swing one way or the other. Now though since the the Mesh was made with Mesh Studio, and the textures are the same, the face is the same as well <sigh>. For Mesh Studio, the answer is simple: Color each side of your prim prototype differently. This will give you two different material faces in your collada file, which are logically the same (in LSL) as different prim faces. Then, your only work is to find which face is which and adjust your code accordingly. One way to look at this is when you "Mesh it!" a simple plywood cube, you will not get the equivalent in the resulting mesh object- you end up with a cube having only one face. To get a cube with six faces, you must color (or texture) each face differently beforehand.
  23. http://www.katsbits.com/tutorials/blender/scene-view-alpha-transparency.php
  24. Fabiano Dover wrote: this is he third topic i see from you thats about a very simple basic skill in sl. Ever thought about looking at the tutorials? or knowledge base? You will find a new world full of things you will encounter in your sl life. Of course you'r free to post your questions here, but playing with the build in tools and settings in your viewer to find out what happens is lot of fun too That is neither blunt nor straight forward- you are bordering on rudeness. If you have a suggestion to help the OP, simply make it without the "you are such a NOOB!" inference, which is neither appropriate nor appreciated here. New residents are not only allowed to ask for help in these forums but should be encouraged to do so, as many times as they need and on as many subjects as they require, to get orientated into Second Life.
  25. Rez a cube, color each side differently and place this script in it: default{ touch_start(integer total_number) { llSetRot (<0.00000, -0.00000, -0.70711, 0.70711>); }} Now, take the cube into inventory, wear it as a HUD, touch it (it'll do a quarter turn and show a differently colored side) then detach it. Drag a copy of the cube from inventory onto the ground and you'll see that the cube has been rotated. But, wear the cube still in inventory again (which will show it to be turned, as expected) and "drop it" onto the ground. That copy will be back to the original orientation.
×
×
  • Create New...