Jump to content

Wulfie Reanimator

Resident
  • Posts

    5,738
  • Joined

Everything posted by Wulfie Reanimator

  1. There are many different functions for sending messages, like llSay/Whisper/Shout, llRegionSay, and llRegionSayTo. http://wiki.secondlife.com/wiki/Category:LSL_Communications You just need a listen event in the object you want to communicate with. http://wiki.secondlife.com/wiki/Listen
  2. Off-topic question, but you don't need any timers at all for AOs anymore, right? The new functions got rid of that hassle.
  3. You would use.. llList2Vector and llList2Rotation with params in the first object to get the values. llSay or llRegionSay/llRegionSayTo to send the values to other objects. llParseString2List or llGetSubString in the second object's listen event to translate the received message into usable values. llSetPrimitiveParams or llSetPos/llSetRegionPos and llSetRot to apply the values in the second object. There are small differences in the functions I suggested that may or may not make a difference in your use-case. Use the wiki to determine which ones fit the best.
  4. You could just add a check at the start of moving_end(): If( dice1 != 0 ) return; This ignores the rest of the event if you already have a result. But I would recommend trying to use something other than moving_start(), because it might trigger sooner than when you want it to. One option is a short timer and/or a llGetVel() check.
  5. What really matters though, is the definition of "nearby" and the exact use-case. (Volume detect prims might be a bit impractical.) A third option is to use llGetAgentlist and check if the target avatar is in that list, and possibly the distance of that avatar from the object. This is a better option than sensors and more versatile than collisions.
  6. For the sake of simplicity, I've taken the simplest to understand script from that old thread and made it a lot easier to read/add to a script. This is a user-defined function. You can copy-paste this to the top of your script, and then use it to give the current die value to whatever you want. integer DiceResult() { rotation current = llGetRot(); vector axis = llRot2Up(current); // Object's local Z axis if(llFabs(axis.z) > 0.5) { if(axis.z > 0) // Top face is up { return 1; } else // Bottom { return 6; } } axis = llRot2Left(current); // Object's local Y axis if(llFabs(axis.z) > 0.5) { if(axis.z > 0) // Left face is up { return 3; } else // Right { return 4; } } axis = llRot2Fwd(current); // Object's local X axis if(llFabs(axis.z) > 0.5) { if(axis.z > 0) // Forward face is up { return 2; } else // Back { return 5; } } return 0; // If you get 0, there was an error. } And to use it, you could simply do: default { touch_start(integer total_number) { llOwnerSay( (string)DiceResult() ); } }
  7. Innula's answer is perfectly fine, but I'd like to share my alternative method. touch_start(integer n) { if(llDetectedKey(0) != llGetOwner()) return; // code as normal } It's short, sweet, and self-contained on one line. The reason why I prefer this method (using return which ends the current function), is simply because this way I do not need to insert brackets and indent my code. And to explain that line a little more in-depth, here's an illustration of the "full length" code and how it becomes that short: if(llDetectedKey(0) != llGetOwner()) { return; } // Which becomes: if(llDetectedKey(0) != llGetOwner()) return; // Which becomes: if(llDetectedKey(0) != llGetOwner()) return; The curly braces of an IF statement can be left out if you only need to execute one statement when the check is successful. Multiple statements can be on the same line, the only thing that matters is the semicolons at the end of each statement. Whenever I leave out the braces, I put the one statement on the same line to avoid confusion that might come from putting it on the line below. This is purely a stylistic choice.
  8. If we're talking about clothing, please don't do timed demos, it makes things very annoying when trying out different demos to see if they fit. If we're talking about scripts, or things where the "meat" of the product is in scripted functionality, timers or limited features are fine. If we're talking about housing, you could use a timer or temp-rez or demo textures. The main thing about making a demo a demo is making it no-modify and adding something to it that makes it a bit obnoxious to use without paying for the full version.
  9. Firestorm's link numbers show correctly after you (re-)rez the object after linking new prims. Scripting a search-loop is a bit overkill if the link is unlikely to change.
  10. The server is "in charge" of everything. Scripts only know what the server knows, because scripts are run entirely server-side. As far as "client-side physics" go and what the client knows about avatar position, there's physics interpolation (client-side prediction between updates from the server), but that's about it. The reason why the bounding box gets shorter is because while ground sitting, your avatar's physical shape becomes about half of the avatar's height. But if you don't account for the height offset, it'll seem like "your legs get shorter." You can offset the bounding box by the avatar's height. (divided by 4, which is half-of-half or 25%) Here's a script you can use to visualize what happens between "avatar size" and "bounding box": (Rez 2 transparent cubes and link them together before putting this script in it. Then wear the cubes on Avatar Center.) default { timer() { list data = llGetBoundingBox(llGetOwner()); vector bb = llList2Vector(data,1) - llList2Vector(data,0); // llOwnerSay(llDumpList2String(data, ", ")); // llOwnerSay("bounding box: " + (string)bb); vector size = llGetAgentSize(llGetOwner()); // llOwnerSay("agent size: " + (string)size); llSetLinkPrimitiveParamsFast(1, [ PRIM_SIZE, size, PRIM_LINK_TARGET, 2, PRIM_SIZE, bb, // This will account for the avatar's bounding box while sitting: PRIM_POSITION, (<0,0,-size.z> * 0.25) ]); } state_entry() { llSetTimerEvent(1); if(llGetAttached()) { llSetLinkPrimitiveParamsFast(-1, [PRIM_POSITION, ZERO_VECTOR]); } } attach(key id) { if(id) { llSetLinkPrimitiveParamsFast(-1, [PRIM_POSITION, ZERO_VECTOR]); } } }
  11. His function isn't the same as llFrand, his function is only pseudo-random. (pseudo just means "not truly") If you do this: default { touch_start(integer total_number) { llOwnerSay( (string)rand() ); } } And touch the object a couple times, then reset the script and touch it again, you'll get the same sequence of the "random" numbers. llFrand doesn't do this. Yeah, you're right, so just cut out the "pre-determined" from what I said and still use raw variables, not a list! (What I mean by "raw" is just "normal" variables, not ones stored in a list.) Even on a non-empty rental sim, running this code (all I did was change llFrand to string and remove two multiplies) finishes as low as 1.8ms and always below 2.5ms. On an empty sim, the llFrand version averaged above 5 ms.
  12. I'm a bit confused. Why are you using strings in a list (I get the string list part) and typecasting llList2Vector to vector, if speed is the most important thing? You've already said the positions don't need to be truly random, just "appear random enough," so you could get away with just pre-determined raw variables. Or even better with Madelaine's function. Haven't double-checked if it's correct and it doesn't include the rest of the code required.
  13. My initial guess is that your method is slower, but uses less memory. (Which doesn't directly affect speed.) But in any case, llFrand is not what anyone would call "laggy." We're talking about a few milliseconds for the entire chunk of code here. My advice is always going to be "just write a functional/readable script first, then look for places to optimize." I ran this test: http://wiki.secondlife.com/wiki/LSL_Script_Efficiency After running this code 5 times, the results for your code were 25.5154 ms in total, 5.10308 ms on average. After running this code 5 times, the results for the code were 26.8786 ms in total, 5.37572 ms on average. So your method is faster but only by a fraction of a millisecond on average. And your method obviously is less random if that matters.
  14. Instead of using temporary attachments (which requires the user to manually accept the prompt), you could just use llGetObjectDetails and OBJECT_POSITION. The result is the same, as llGetPos returns the wearer's position in region coordinates, not the position of the attachment (ever). But that still doesn't solve anything. It's just not possible to get an attachment's region coordinates, or relative position to avatar center (avatar's actual position). Even if the avatar is standing completely straight (no rotation on any bones), you still can't predict where any specific part of the body is. At best you can pick a proportional height (say at 46.5% of their height) and just assume that that is where every avatar's waist will be. It might make initial guesses more accurate, but won't eliminate the need for adjusting, so to put it bluntly it would be wasted effort over just default pose offsets which is much simpler to implement.
  15. I am just the worst at skim reading.
  16. If you have a range where the "mid point" is 0, it's very simple to generate a random number on that range. llFrand(range) - (range/2); // Or just: llFrand(0.02) - 0.01; The range Bewm wants is 0.02 because it is the total difference between -0.01 and 0.01. This way, if llFrand returns 0, the texture will be moved by -0.01, and if llFrand returns 0.02, the texture will move by 0.01.
  17. An avatar only has one hitbox which is what is detected by collisions. The hitbox is centered at the avatar's raw position and is unaffected by animations. You can make these visible under Developer > Render Metadata > Avatar Hitboxes There isn't a way to detect where exactly the waist is (which would probably be the most important part for pose adjusting) or any other specific part of the avatar.
  18. All good suggestions. What I also wanted to do was add a way to toggle the acceptable ratings, but got distracted and lost interest soon after. The main goal was to simplify the original script though, not necessarily make a polished product. Anyone is free to take it from here and make it their own. Also, generally if it's a group of friends sim hopping, the group tends to pick a specific person to do the teleporting. (Just speaking from personal experience, we never needed any turn taking or such because the sim was random regardless.) And adding an offset is mostly pointless as most sims have their own landing spot where avatars will pile up regardless of where the teleport command sends them.
  19. This is just something I worked on briefly after seeing an old full-perm sim hopper script that was over 1000 lines long and very hard to follow because of how fragmented it was. What the script does, is it requests random sim names from gridsurvey.com and checks the maturity rating before using llRegionSay to countdown and tell all hoppers to teleport to that location. How to use: - (Optional) Change your preferences in the script: Acceptable sim ratings (general, moderate, adult) Max retries (how many random sims to request before aborting) Countdown before teleport The private "script channel" to use (everybody on the same channel will be teleported) - Put the script into any attachment (creating a new object is recommended) - Say "hop" on the chat command channel (default: 1) - Wait for the script to find a sim with an appropriate rating string QUERY_URL = "http://api.gridsurvey.com/simquery.php?region="; string RANDOM = "FETCH_RANDOM_ONLINE_REGION_FROM_DATABASE"; key SND_BEEP = "11a3f4d6-75ea-acf3-29a7-41bcba51a9d6"; // Chat commands go here. integer chat_channel = 1; // Commands intended for/by scripts only. integer script_channel = -92403; // used in TeleportCheck integer allow_adult = 1; integer allow_moderate = 0; integer allow_general = 0; // init in listen, used in dataserver // How many tries to find a random server? integer max_retries = 10; integer retries_left; // init in dataserver, used in timer // Countdown before actually teleporting integer max_timer = 5; integer time_left; key query_sim_name; // for llHTTPRequest key query_sim_rating; // for llRequestSimulatorData key query_sim_pos; // for llRequestSimulatorData // sim info integer data_received; // used in dataserver string target_name; // from http_response string target_rating; // from dataserver vector target_pos; // from dataserver GetSimData(string sim_name) { target_pos = <0,0,0>; target_rating = ""; data_received = 0; query_sim_rating = llRequestSimulatorData(sim_name, DATA_SIM_RATING); query_sim_pos = llRequestSimulatorData(sim_name, DATA_SIM_POS); } integer TeleportCheck(string rating, vector pos) { if(pos == ZERO_VECTOR) { llOwnerSay("TP failed, invalid coordinates!"); return 0; } if( (rating == "ADULT" && !allow_adult) || (rating == "MATURE" && !allow_moderate) || (rating == "PG" && !allow_general) || (rating == "UNKNOWN")) { llOwnerSay("TP failed, invalid maturity rating"); return 0; } else return 1; } default { listen(integer c, string n, key id, string m) { if(c == chat_channel) { if(m == "hop") { llOwnerSay("Looking for a sim..."); retries_left = max_retries; query_sim_name = llHTTPRequest(QUERY_URL + RANDOM, [], ""); } } else if(c == script_channel) { string cmd = llGetSubString(m, 0, 3); // first 4 chars string data = llGetSubString(m, 4, -1); // the rest if(cmd == "TICK") { // llOwnerSay((string)data + "s to TP!"); llPlaySound(SND_BEEP, 1); } if(cmd == "TELE") { target_pos = (vector)data; llTeleportAgentGlobalCoords(llGetOwner(), target_pos, <128, 128, 64>, <0,0,0>); } } } http_response(key request, integer status, list metadata, string body) { if(request == query_sim_name) { GetSimData(target_name = body); } } dataserver(key request, string data) { if(request == query_sim_rating) { target_rating = data; query_sim_rating = NULL_KEY; ++data_received; } else if(request == query_sim_pos) { target_pos = (vector)data; query_sim_pos = NULL_KEY; ++data_received; } // Dataserver requests may not be answered in order.. // so we keep count of how many responses we've got. if(data_received == 2) { // Let's check whether the target sim meets our criteria. if(!TeleportCheck(target_rating, target_pos)) { // If not, and we still have retries left, try again. if(retries_left--) query_sim_name = llHTTPRequest(QUERY_URL + RANDOM, [], ""); else llOwnerSay("Giving up on retries!"); } else { // If the sim passes our criteria, start the countdown timer. llOwnerSay("Found a sim!"); time_left = max_timer; llSetTimerEvent(1); query_sim_name = NULL_KEY; } } } timer() // Just beep beep like a sheep until teleport. { if(--time_left) { llPlaySound(SND_BEEP, 1); llRegionSay(script_channel, "TICK" + (string)time_left); // llOwnerSay((string)time_left + "s to TP!"); } else { llSetTimerEvent(0); llRegionSay(script_channel, "TELE" + (string)target_pos); llTeleportAgentGlobalCoords(llGetOwner(), target_pos, <128, 128, 64>, <0,0,0>); } } state_entry() { llListen(chat_channel, "", llGetOwner(), ""); // for chat commands llListen(script_channel, "", "", ""); // for scripts llRequestPermissions(llGetOwner(), // cheat no-script areas PERMISSION_TELEPORT|PERMISSION_TAKE_CONTROLS); } attach(key id) { if(id) llRequestPermissions(llGetOwner(), PERMISSION_TELEPORT|PERMISSION_TAKE_CONTROLS); } changed(integer change) { if(change & CHANGED_OWNER) llResetScript(); } } For anyone who's interested in seeing the source that inspired this, see: http://puu.sh/yjTa7/79f2fd40bc.txt (But be wary)
  20. This may be nitpicking, but since the script doesn't seem to work as-is and reading it seems confusing, I tried making my own implementation and after I was done with it I came to the conclusion that your method seems a little over-complicated. Maybe I'm just too bad at math to get it at first glance. Basically all of WhereAmI() is confusing me. This is what I came up with (works linked and unlinked, put the script directly into the door, door is assumed to be "cut in half" like Rolig's): integer open; integer swing_deg = 90; integer swing_time = 5; rotation rot_closed; default { state_entry() { rot_closed = llGetLocalRot(); } touch_start(integer total_number) { if(open) return; // Ignore clicks while open. vector avatar_position = llDetectedPos(0); vector avatar_direction = llVecNorm(avatar_position - llGetPos()) / llGetRot(); // llOwnerSay((string)avatar_direction); // Front face of the door is towards the outside. if( avatar_direction.x > 0 ) { llOwnerSay("Welcome!"); llSetLocalRot(llGetLocalRot() * llEuler2Rot(<0,0,swing_deg*DEG_TO_RAD>)); } else { llOwnerSay("Goodbye!"); llSetLocalRot(llGetLocalRot() * llEuler2Rot(<0,0,-swing_deg*DEG_TO_RAD>)); } open = TRUE; llSetTimerEvent(swing_time); } timer() { llSetLocalRot(rot_closed); open = FALSE; llSetTimerEvent(0); } } The script won't need a reset even if the linkset is rotated. A reset is only needed if the door itself changes position or needs a new (local) closed rotation within the linkset.
  21. I'm pretty sure that even if you made a commercial product (like an applier) for someone else's product that wasn't intended to support it, you wouldn't get into trouble. All it would take for the creator to break your applier was to update their product. Whatever happens after that is up to you, the original creator, and both of your customers. That's not really the point though, the point is, I'm pretty confident none of this breaks any rules set by LL.
  22. Sublime Text 2 all the way. So many useful plugins you can use for writing your script. I use things like: - LSL syntax highlighting (for proper color schemes) - Autocomplete, even from other files (I use the LSL preprocessor features and my own utility snippets) - Multiple cursors (text editing in multiple places at once, list formatting, etc) - Column ruler (to prevent yourself from writing lines that are too long) - Code minimap (to easily scroll through and find where of your code is) - Code folding (hide long bits of code you don't need to see) - Code alignment (makes your variables look all pretty and organized) - Line shifting (up/down) and line sorting (for lists) - Highlighting/removing trailing spaces - More stuff I can't think off the top of my head.. None of those features are exclusive to Sublime, but it's also lightweight and fully portable. Here's an example of what a "big script" looks like for me. http://puu.sh/y7TdY/65ad97cfca.png (Relatively complex bolt-action rifle) http://puu.sh/y7Uw1/dfbb4688d9.png (Relatively simple list-based text-to-symbol converter)
  23. Scripts have no access whatsoever to anything in a user's inventory. However you could just tell people that the HUD must be worn to get the tickets and change the HUD's own description, but there is a small problem with permissions. You would need to make the HUD no-modify and no-copy. If it can be modified, the user can obviously change the amount of tickets they have. If it can be copied, the user can earn lots of tickets and make infinite copies of the object, gaining infinite tickets. This leaves the transfer permission, which you can't also disable, since you can't disable all permissions on an object. Since users would be able to trade their HUDs (with tickets), it's possible someone might try to profit from selling tickets directly. These are the kind of things you'll have to keep in mind while trying to design any kind of system, LSL on its own isn't exactly secure unless you get really clever. c: Also, to me it sounds like you're getting really excited about scripting and that's awesome, but trying to script an entire arcade is a pretty big and complex project. You can easily take too big of a bite and choke on it (or burn yourself out due to constant headaches and getting stuck). I would suggest you focus on smaller things, like trying to just script a simplified version of the ticket system that allows you to gain/spend tickets through a second object that doesn't do anything but give/take tickets.
  24. If you're just looking to do the arcade for fun rather than some serious business, you could simply make a HUD or something that stores the data about tickets and whatnot. The same HUD could work as a relay to any of the machines in the arcade, checking how many tickets are left and handling transactions. As long as the scripts in the HUD are not reset, it'll be fine.
  25. In LSL, all variables are implicitly given a default value when they're declared. integer = 0 float = 0.0 vector = <0,0,0> rotation = <0,0,0,1> string = "" key = "00000000-0000-0000-0000-000000000000" list = []
×
×
  • Create New...