Jump to content

SmacemanSpiff Grau

Resident
  • Posts

    12
  • Joined

  • Last visited

Everything posted by SmacemanSpiff Grau

  1. I am finding this with the KFM_CMD_PAUSE and KFM_CMD_PLAY. It never stops or starts exactly where it "should", so the more I use it, the more off kilter the object gets. I did play around with way points, but I think my approach was messy. I was using targets and different llSetKeyframedMotion commands at each target. Because it is executing just shy of the actual marker, again, the route gets thrown off. Similar to Profaitchikenz, it eventually never makes it to the next target and stalls out. Your wording changed my approach to it, though. I think I have an idea, or the start of one, to have a list way points, figure out which one is closest, move to that position / rotation, clear the llSetKeyframedMotion list and generate a new one based on the waypoint. This will eliminate the use of the PAUSE and PLAY commands and just use the STOP command. Another idea, and probably easier than doing lengthy math to determine the closest way point, is to utilize targets. Thanks for your thoughts!
  2. Qie - Thank you so much! That fixed it! At least in my first tests. Looks like people can still right-click to get the pie menu, but it prevents them from grabbing it. There are only two of us with edit rights, so it should be great. I played with the PAUSE and PLAY commands some more, and the more you start and stop, the object does start getting a little wonky and off track. I will probably have to build in a reset function at a target location to keep it clean. Here is a "basic" version of the working script. ////// VARIABLES key ActiveID; integer listenHandle_A; vector sit_here = <-0.3, 0.0, 0.55>; string animation = "TankSitterV6"; integer KeyframeActive = FALSE; integer KeyframePause = FALSE; ////// FUNCTIONS rotation NormRot(rotation Q) { float MagQ = llSqrt(Q.x*Q.x + Q.y*Q.y +Q.z*Q.z + Q.s*Q.s); return <Q.x/MagQ, Q.y/MagQ, Q.z/MagQ, Q.s/MagQ>; } StartFlightPath() { llSetRegionPos(<144.88700, 144.00000, 501.87710>); llSetRot(<-0.00000, 0.00000, -1.00000, 0.00000>); llSleep(2.0); // making sure position and rotation are set before taking off llSetKeyframedMotion( [ <-33.38704, 0.00000, 0.00000>, NormRot(<0.00000, 0.00000, 0.00000, 1.00000>), 6.677408, <-7.57170, -6.14423, 0.00000>, NormRot(<0.00000, 0.00000, 0.70711, 0.70711>), 1.950202, <9.88572, -7.05081, 0.00000>, NormRot(<0.00000, 0.00000, 0.70711, 0.70711>), 2.428509, <31.07302, 0.00000, 0.00000>, NormRot(<0.00000, 0.00000, 0.00000, 1.00000>), 6.214604, <4.32674, 6.56656, 0.00000>, NormRot(<0.00000, 0.00000, 0.70711, 0.70711>), 1.572773, <-4.32674, 6.62848, 0.00000>, NormRot(<0.00000, 0.00000, -0.70711, -0.70711>), 1.583128 ], // [KFM_MODE, KFM_LOOP]); } PauseFlightPath() { llSetKeyframedMotion( [], [ KFM_COMMAND, KFM_CMD_PAUSE]); } ResumeFlightPath() { llSetKeyframedMotion( [], [ KFM_COMMAND, KFM_CMD_PLAY]); } StopFlightPath() { llSetKeyframedMotion( [], [ KFM_COMMAND, KFM_CMD_STOP]); } ////// MENUS Menu_A(key id) { // MAIN MENU string text = "\nMain Menu\n\n"; list buttons; // if KeyframActive is not active, no reason to play or pause if(!KeyframeActive && !KeyframePause) buttons = [ "Reset", "EXIT"]; // if KeyframActive is active, allow pause else if(KeyframeActive && !KeyframePause) buttons = ["Stop", "Pause", "EXIT", "Reset"]; // if KeyframActive is active and has been paused, allow play else if(KeyframeActive && KeyframePause) buttons = [ "Stop", "Resume", "EXIT", "Reset"]; llDialog(id, text, buttons, Channel_A()); } ////// CHANNELS integer Channel_A() { integer channel; listenHandle_A = llListen (channel = -1 - (integer)("0x" + llGetSubString( (string)llGetKey(), -8, -2) ), "", ActiveID, ""); llSetTimerEvent (60.0); return channel; } Close_Channel_A() { llListenRemove (listenHandle_A); llSetTimerEvent (0.0); } ////// SCRIPT BODY default { state_entry() { llSitTarget(sit_here, ZERO_ROTATION); llSetStatus( STATUS_BLOCK_GRAB_OBJECT, TRUE); // THANKS QIE! llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]); } touch_start(integer total_number) { ActiveID = llDetectedKey(0); Menu_A(ActiveID); } changed(integer change) { if (change & CHANGED_LINK) { key agent = llAvatarOnSitTarget(); if (agent) { llRequestPermissions(agent, PERMISSION_TRIGGER_ANIMATION); } else { llStopAnimation(animation); } } } run_time_permissions(integer perm) { if (perm) { llStartAnimation(animation); } } listen(integer channel, string name, key id, string message) { if (channel == Channel_A()) { Close_Channel_A(); if(message == "Reset") { StopFlightPath(); StartFlightPath(); KeyframeActive = TRUE; KeyframePause = FALSE; Menu_A(id); } else if(message == "Pause") { PauseFlightPath(); KeyframeActive = TRUE; KeyframePause = TRUE; Menu_A(id); } else if(message == "Resume") { ResumeFlightPath(); KeyframeActive = TRUE; KeyframePause = FALSE; Menu_A(id); } else if(message == "Stop") { StopFlightPath(); KeyframeActive = FALSE; KeyframePause = FALSE; Menu_A(id); } } } timer() { Close_Channel_A(); } }
  3. So, let’s just imagine I’m a farmer. I'm not, but let's imagine. Let's also imagine that as a farmer I have a big field. On this field there is a tractor. This tractor will: - NOT be a vehicle, but act as a “tour bus” when someone sits on it - Sit idle in the field until someone clicks on it to ride it. - The tractor will then either sit idle for a picture opportunity or drive itself and the rider in a preset loop around the field. The rider will also be able to pause the tractor route via the menu if they see a bird or a cow (because cows are fun to stop and look at). - When the rider stands up, the tractor will stay in position waiting for the next person to sit on it with the ability to resume the preset route from where it left off. - It will NOT run the preset route if nobody is riding it, because that would be a rogue tractor. SAFTEN UP, PEOPLE! I am currently working with llSetKeyframedMotion, which comes SO CLOSE to doing what I want. Everything is working, including the sit position, except for one component. I have a preset loop programmed, which works. I use KFM_CMD_PAUSE to pause the tractor in route and KFM_CMD_PLAY to resume, which also works. The caveat is that when it is in PAUSE mode and someone right clicks the tractor to sit or stand and then de-selects it, it automatically resumes the KeyframedMotion. This is stated in the wiki, and proven by me (obviously the final word… ). I can program the tractor to SIT ON TOUCH, but we all know that people will still right click to get the pie menu to sit on it or perv out the details (at least, I would…). Is there any way around the “resume KeyframedMotion after selection” caveat? I’ve tried thinking of a way to stop the keyframe command and then restart it from where it left off (basically pausing, but actually stopping the command), but my brain hasn’t gotten there. The biggest issue is if it is stopped in the middle of a turn there is no easy way of picking back up from there. I tested using single instances of llSetKeyframedMotion for each leg of the route, starting a new one once it reaches a target, but this got messy really fast and has potential for a TON of script, which has more potential for errors and offsets. I considered using llMoveToTarget, but I didn’t really want to make it a physical object, and llSetKeyframedMotion is so much smoother and easier to work with. I have not played with any “roller coaster” style scripts yet, but I’m afraid any prim track I use would eat up all my available prim. It’s a homestead, but still… At this point, my last resort is just putting out a rezzer that will only allow one tractor at a time on property. Once the rider gets off, it will die and rez a new one at the rez point. This will eliminate the “pause and resume” option after the rider stands up and kind of defeats the whole purpose I set out with. I’m not thrilled about this idea, but it is a backup plan. The cows and I thank you for any suggestions.
  4. Thanks, KT. That does make sense. I had read through the wiki but wanted to see if I was missing something. The whole concept is starting to get clearer in my head the more I look it over and read other blogs about it. Not exactly what I was looking for, but glad to get a little familiar with it.
  5. I'm just starting to understand RLV, and want to know if this can be used for other avatars? Basically, if another avatar clicks on an object owned by me, can RLV force them to sit on another unlinked prim? I tried it with an alt, and when the alt clicked the object, it sat ME on the unlinked prim - which makes sense with the llOwnerSay() function. I tried it with llInstantMessage() function, but it just IMed the toucher the RLV text. Thanks!
  6. Ron - I tested the script before posting and it worked for me, unless I missed a step. I didn't quite get the llGetInventoryType usage, but now I do. My brain read the first part of Qie 's post and then filled in the rest on its own - not something new, unfortunately. I'll take another look to get it right. Ruthven - thanks for the add on. In my full script I do have the changed event to catch that exact possibility.
  7. Qie - THANK YOU SO MUCH!!! I did not know that, and will most likely forget about it the next time... but I was able to put a work around in place and BINGO! I put the llGetInventoryKey() in the state_entry to return a value. That will save a lot of broken keyboards and mice on my side! // Check for a valid existant notecard, and read it into a list // On touch, say the total number of lines, number of lines containing data, and say each line // Omei Qunhua 7-Jan-2013 string gNotecard = "BingoWords"; // Name of notecard to be examined key gLineRequestID; // Identity of expected line count dataserver event key gReadRequestID; // Identity of expected data read dataserver event integer gLineTotal; // The number of lines in the NC, as determined by this script integer gLineIndex; // Index for data read requests list gDataLines; // List containing all data lines from notecard, excluding blank and comment lines string gStatus; // Will contain EOF when notecard reading has finished integer notecardLine; integer activeCard = FALSE; default { state_entry() { if (llGetInventoryKey(gNotecard) ) activeCard = TRUE; } changed(integer change) { if (change & CHANGED_INVENTORY) llResetScript(); } dataserver(key query_id, string data) // NOTE: Each dataserver request generates a new unique ID Key { if (query_id == gLineRequestID) // if the requested key is for the number of total lines { gLineTotal = (integer) data; // Cast the data string to an integer to get the number of lines gReadRequestID = llGetNotecardLine(gNotecard, gLineIndex); // Request a read of the first notecard line llOwnerSay(gNotecard + " had a total of " + (string) gLineTotal + " lines." ); llOwnerSay("Please stand by while the notecard is being read..." ); } else if (query_id == gReadRequestID) // if the requested key is for the data for a line { if (data == EOF) { integer x = 0; integer count = (gDataLines != [] ); for ( ; x < count; ++x) // Loop through the data list { llOwnerSay( llList2String(gDataLines, x) ); } llOwnerSay(gNotecard + " had a total of " + (string) gLineTotal + " lines, of which " + (string) count + " contained data." ); } else { gDataLines += data; // Add this data line to our global list // bump line number for reporting purposes and in preparation for reading next line ++gLineIndex; if (data == "" || llGetSubString(data, 0, 0) == "#") ++gLineIndex; // ignore blank or comment lines gReadRequestID = llGetNotecardLine(gNotecard, gLineIndex); } } } touch_start(integer total_number) { gDataLines = []; gLineIndex = 0; if (activeCard) // Test if notecard exists and has been saved (returned key will be NULL_KEY otherwise) { gLineRequestID = llGetNumberOfNotecardLines(gNotecard); // Kick off a request for the total number of lines that the notecard contains } else llOwnerSay("Notecard '" + gNotecard + "' does not exist or has no saved data"); } }
  8. Well, something in my script is causing an issue then. I tried it with the script below and it would only read the note card if the card is full perm. Again - this is testing after giving the objects to a second avatar. (hopefully the gyazo link will work...) I named the 5 test prims according to the note card permissions. The object and script are all full perm in each case. And, yes, the note card is named correctly in all objects. I have also tried on different sims, sandboxes and my own land. https://gyazo.com/3305b5e3680fc8a4162a8ccb32c9bd53 // Check for a valid existant notecard, and read it into a list // On touch, say the total number of lines, number of lines containing data, and say each line // Omei Qunhua 7-Jan-2013 string gNotecard = "BingoWords"; // Name of notecard to be examined key gLineRequestID; // Identity of expected line count dataserver event key gReadRequestID; // Identity of expected data read dataserver event integer gLineTotal; // The number of lines in the NC, as determined by this script integer gLineIndex; // Index for data read requests list gDataLines; // List containing all data lines from notecard, excluding blank and comment lines string gStatus; // Will contain EOF when notecard reading has finished integer notecardLine; default { state_entry() { } changed(integer change) { if (change & CHANGED_INVENTORY) llResetScript(); } dataserver(key query_id, string data) // NOTE: Each dataserver request generates a new unique ID Key { if (query_id == gLineRequestID) // if the requested key is for the number of total lines { gLineTotal = (integer) data; // Cast the data string to an integer to get the number of lines gReadRequestID = llGetNotecardLine(gNotecard, gLineIndex); // Request a read of the first notecard line llOwnerSay(gNotecard + " had a total of " + (string) gLineTotal + " lines." ); llOwnerSay("Please stand by while the notecard is being read..." ); } else if (query_id == gReadRequestID) // if the requested key is for the data for a line { if (data == EOF) { integer x = 0; integer count = (gDataLines != [] ); for ( ; x < count; ++x) // Loop through the data list { llOwnerSay( llList2String(gDataLines, x) ); } llOwnerSay(gNotecard + " had a total of " + (string) gLineTotal + " lines, of which " + (string) count + " contained data." ); } else { gDataLines += data; // Add this data line to our global list // bump line number for reporting purposes and in preparation for reading next line ++gLineIndex; if (data == "" || llGetSubString(data, 0, 0) == "#") ++gLineIndex; // ignore blank or comment lines gReadRequestID = llGetNotecardLine(gNotecard, gLineIndex); } } } touch_start(integer total_number) { gDataLines = []; gLineIndex = 0; if (llGetInventoryKey(gNotecard) ) // Test if notecard exists and has been saved (returned key will be NULL_KEY otherwise) { gLineRequestID = llGetNumberOfNotecardLines(gNotecard); // Kick off a request for the total number of lines that the notecard contains } else llOwnerSay("Notecard '" + gNotecard + "' does not exist or has no saved data"); } } Thanks, Smace
  9. I think I answered my own question, but am hoping for some confirmation to the contrary. Using the data server event and llGetNotecardLine, can the note card in the object's inventory be NO MOD? The SL wiki says "The notecard read can be no modify, or no modify/no copy.", however, my testing says otherwise. http://wiki.secondlife.com/wiki/LlGetNotecardLine When testing with my original account, the script will work fine and return the requested information. When I send the exact same object with the exact same note card (set to no copy / no mod) to my testing alt, it returns "Notecard 'BingoWords' does not exist or has no saved data" in general chat. When the note card is FULL PERM - it works as it should for the test account. Before I dumb down the script to post for trouble shooting, SHOULD it be working as no copy / no mod? Or am I just wishing way too hard for something that I've already proven is not possible? Thanks for any insight. Smace
  10. Thank you for the suggestions. Fenix - I will heed your warning. I tend to live in a bubble with good friends and forget about the "ugly second lifers" that take a fun toy and abuse it. For personal satisfaction, I'm still curious if I can get it to work. Thanks!
  11. I figured, but it was worth asking. What if the object is mobile? Like a weapon? I have thought of using a sensor or shooting an object in addition to the particles that will then react if it collides with an avatar, but neither one sounds exciting. Thanks for the quick response!
  12. Is it possible to trigger a collision event if an avatar touches particles emitted by an object? Example: Joe walks by my box that is shooting out sparks. One of the sparks hits Joe (Joe never touches the actual box) and he then catches on fire. While I feel bad for Joe because he wasn't wearing flame retardant mesh clothing, I'm curious if this is possible. If not, any suggestions on how to work around it? Thanks for any direction. Smace
×
×
  • Create New...