{"id":486,"date":"2018-08-28T14:24:12","date_gmt":"2018-08-28T21:24:12","guid":{"rendered":"http:\/\/www.caledonoxbridge.org\/guardian\/?p=486"},"modified":"2018-09-10T13:01:26","modified_gmt":"2018-09-10T20:01:26","slug":"lsl-scripting-whichever-way-the-wind-blows","status":"publish","type":"post","link":"https:\/\/www.caledonoxbridge.org\/guardian\/2018\/08\/28\/lsl-scripting-whichever-way-the-wind-blows\/","title":{"rendered":"LSL Scripting \u2014 Whichever Way the Wind Blows"},"content":{"rendered":"<p>In the prior and <a href=\"http:\/\/www.caledonoxbridge.org\/guardian\/2018\/08\/25\/lsl-scripting-day-night-and-the-in-between\/\" target=\"_blank\" rel=\"noopener\">fourth article<\/a> in this series of articles on scripting in Second Life\u2122, we looked at a script to change the color of an object based on the position of the SL sun.The notable properties of the script were use of a timer event and calling a built-in function to get the sun direction. Today&#8217;s example starts out quite similar in structure and then extends things a bit.<\/p>\n<p>Within my small parcel of land in Caledon Stormhold, I have a wind vane, since SL simulates horizontal winds. Although they aren&#8217;t the best winds for sailing, one can even use the default SL winds for that purpose. So having a wind vane is more than just a silly exercise in decoration. Moreover, a wind vane is supposed to point in the direction of the wind, and the direction of the wind varies. With that thought, we come to needing a script and thus have an example to work through.<\/p>\n<p>There&#8217;s not much to keep track of at the global level for this script, just the time interval on which to check the wind direction and align the x-direction of the wind vane to the wind. We can write<\/p>\n<pre>float INTERVAL = 3.0;   \/\/ Interval at which to update the wind vane direction (seconds)\r\n<\/pre>\n<p>and be done with our global variables (for the moment).<\/p>\n<p>We will want a user-defined function, one that I&#8217;ll call <em>uuAlign2Wind<\/em>, but I&#8217;m going to skip over that at present and look at our default state and the needed event-handlers. This is going to look very familiar if you read the last article.<\/p>\n<pre style=\"font-size: 0.8em; font-weight: bolder; border-top: 1px solid; border-bottom: 1px solid;\">default {\r\n   state_entry () {\r\n      uuAlign2Wind ();\r\n      llSetTimerEvent (INTERVAL);\r\n   }\r\n\r\n   timer () {\r\n      uuAlign2Wind ();\r\n   }\r\n}\r\n<\/pre>\n<p>What we have is a common structure for scripts that are purely timer-driven: initialize and set a timer in state_entry (i.e. when the script enters the default state), then do an update on each timer event. The details that make one application different from another are all hidden in the user defined function, which makes the underlying structure of the script easy to see.<\/p>\n<p>Now let&#8217;s make this a bit more interesting and assume we are writing the script for someone else. At this point, you get an IM from the person saying &#8220;I forgot to tell you I want to be able to turn this on and off and only I should be able to do this&#8221;. You make a reply like &#8220;Oh, okay&#8221; (eye-rolls are .optional). Let&#8217;s see how we can handle this.<\/p>\n<p>I&#8217;m going to add a global variable to keep track of whether our wind vane is on or off and add an event-handler for the start of a touch. Let&#8217;s see what that gives us so far.<\/p>\n<pre style=\"font-size: 0.8em; font-weight: bolder; border-top: 1px solid; border-bottom: 1px solid;\">float   INTERVAL = 3.0;   \/\/ Interval at which to update the wind vane direction (seconds)\r\ninteger isActive = 0;     \/\/ Flag to keep track of on\/off status; 0 means inactive\r\n\r\nuuAlign2Wind () {\r\n   ;   \/\/ Just a stub for now \r\n}\r\n\r\ndefault {\r\n\r\n   state_entry () {\r\n      llOwnerSay (\"Wind Vane Inactive\");\r\n   }\r\n\r\n   on_rez (integer rez_param) {\r\n      llResetScript ();\r\n   }\r\n\r\n   touch_start (integer num_touches) {\r\n\r\n      if ( llDetectedKey(0) == llGetOwner() ) {\r\n         \/\/ We were touched by our owner, so we respond\r\n\r\n         \/\/ Flip our activity status: 1 goes to 0; 0 goes to 1\r\n         isActive = 1 - isActive;\r\n\r\n         if ( isActive ) {\r\n            uuAlign2Wind ();\r\n            llSetTimerEvent (INTERVAL);\r\n         } else {\r\n            llSetTimerEvent (0.0);\r\n         }      \r\n      }\r\n   }\r\n\r\n   timer () {\r\n      uuAlign2Wind ()\r\n   }\r\n} \r\n<\/pre>\n<p>We made some changes up above. Let&#8217;s run through them. We don&#8217;t have much to do in <em>state_entry<\/em> now. What was there before in now part of the <em>touch_start<\/em> event, With two possible modes, however, it&#8217;s good to let the owner know whether we are active or inactive when we start up. Sending a message to the object\/script owner is the purpose of <em>llOwnerSay<\/em>.<\/p>\n<p>Since we want a predictable status when we rez the wind vane, we use the <em>on_rez<\/em> event to reset the script. This occurs even before <em>state_entry.<\/em> We can rez the wind vane, it resets the script, lets the owner know it&#8217;s not active, and then it just sits there. Sitting there may be attractive, but it&#8217;s not why wind vanes were built (something similar has been said about ships). As a background note, the default thing for a script to do, when the object it&#8217;s in is taken into inventory and then rerezzed, is to simply continue from where it was.<\/p>\n<p>The <em>touch_start<\/em> event is triggered on the start of a touch. It has the ability to handle multiple near simultaneous touches as one event and receives an integer parameter saying how many such touches were bundled. This is relatively rare and we can ignore that possibility for our application. We only look at the touch with an index of zero.<\/p>\n<p>Touch, collision, and sensor events each have a set of <a href=\"http:\/\/wiki.secondlife.com\/wiki\/Category:LSL_Functions\" target=\"_blank\" rel=\"noopener\">llDetected\u2026<\/a> routines to gain more information about what was detected. The values returned by these routines are only valid while in the single event. If you want to save any values, you have to store them in global variables.<\/p>\n<p>The built-in function <em>llDetectedKey,<\/em> called with an argument of zero, returns the key (or UUID) of the avatar that touched it. The built-in function <em>llGetOwner<\/em> returns the key of the owner. If those are equal (tested with the <a href=\"http:\/\/wiki.secondlife.com\/wiki\/LSL_Operators\" target=\"_blank\" rel=\"noopener\">operator<\/a> ==), then we&#8217;ve been touched by the owner. If not, we simply ignore the touch. You might want to review the <em><a href=\"http:\/\/wiki.secondlife.com\/wiki\/If\" target=\"_blank\" rel=\"noopener\">if-else construct<\/a><\/em> if you .are not familiar with such <em>flow-control<\/em>.<\/p>\n<p>The new global variable <em>isActive<\/em> is an integer that was initialized to have the value zero. In LSL, a zero tests as <em>false<\/em> and a one tests as <em>true<\/em>. On a valid touch, we flip the value of isActive by setting it to one minus itself (1-0 equals 1; 1-1 equals zero). If the subsequent value of isActive is one, we do what we did before in <em>state_entry<\/em>, initializing the wind vane direction and setting a timer. If the value is zero, we turn off the timer by giving it an interval of zero. What remains unchanged is the timer event to update the wind vane direction. That completes everything we need except the function <em>uuAlign2Wind<\/em>, which is next.<\/p>\n<p>There&#8217;s a built-in function <em>llWind<\/em> to get the vector wind (3 floats with the z component zero) and another function l<em>lVecNorm<\/em> to normalize a vector to have a length of one (unit length). llWind takes a vector offset from the object as a parameter, but we give it a zero vector (there&#8217;s a built-in constant) for that, since we want the wind at the wind vane. Using the X and Y direction cosines resulting from the above, we can call <em>llAtan2<\/em> to get the rotation of the wind direction about the Z\u2013axis. Finally, there&#8217;s\u00a0 a simple formula for directly writing the corresponding <a href=\"http:\/\/wiki.secondlife.com\/wiki\/Rotation\" target=\"_blank\" rel=\"noopener\">quaternion rotation<\/a> for a rotation around the Z\u2013axis. All that remains is to use that to set the wind vane rotation. Let&#8217;s put it all together.<\/p>\n<pre style=\"font-size: 0.8em; font-weight: bolder; border-top: 1px solid; border-bottom: 1px solid;\">float   INTERVAL = 3.0;   \/\/ Interval at which to update the wind vane direction (seconds)\r\ninteger isActive = 0;     \/\/ Flag to keep track of on\/off status; 0 means inactive\r\n\r\nuuAlign2Wind () {\r\n   vector   wind   = llVecNorm(llWind(ZERO_VECTOR));\r\n   float    zangle = llAtan2(wind.y, wind.x);\r\n   rotation wndrot = &lt;0.0, 0.0, llSin(0.5*zangle), llCos(0.5*zangle)&gt;;\r\n   llSetRot(wndrot);\r\n}\r\n\r\ndefault {\r\n\r\n   state_entry () {\r\n      llOwnerSay (\"Wind Vane Inactive\");\r\n   }\r\n\r\n   on_rez (integer rez_param) {\r\n      llResetScript ();\r\n   }\r\n\r\n   touch_start (integer num_touches) {\r\n\r\n      if ( llDetectedKey(0) == llGetOwner() ) {\r\n         \/\/ We were touched by our owner, so we respond\r\n\r\n         \/\/ Flip our activity status: 1 goes to 0; 0 goes to 1\r\n         isActive = 1 - isActive;\r\n\r\n         if ( isActive ) {\r\n            uuAlign2Wind ();\r\n            llOwnerSay (\"Wind Vane Active\");\r\n            llSetTimerEvent (INTERVAL);\r\n         } else {\r\n            llSetTimerEvent (0.0);\r\n            llOwnerSay (\"Wind Vane Inactive\");\r\n         }\r\n      }\r\n   }\r\n\r\n   timer () {\r\n       uuAlign2Wind ();\r\n   }\r\n}\r\n<\/pre>\n<p>With that, we have a script for rotating a Wind Vane or a flag to align with the SL wind and that allows the owner to turn it on and off with a touch. If you&#8217;ve been following along with the series, you&#8217;ve learned a fair amount about scripting. Try your own ideas and see what you run into. Thank you for your attention. The <a href=\"http:\/\/www.caledonoxbridge.org\/guardian\/2018\/09\/10\/lsl-scripting-touch-my-face\/\" target=\"_blank\" rel=\"noopener\">sixth article<\/a> will be on links and faces.<\/p>\n<p>Caledon Oxbridge also has an inworld <strong>Basic Scripting<\/strong> class, Mondays at noon SLT in our <a href=\"http:\/\/maps.secondlife.com\/secondlife\/Caledon%20Oxbridge\/78\/140\/29\" target=\"_blank\" rel=\"noopener\">main lecture hall<\/a>. Please see our <a href=\"http:\/\/www.caledonoxbridge.org\/class_schedule.php\" target=\"_blank\" rel=\"noopener\">schedule page<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the prior and fourth article in this series of articles on scripting in Second Life\u2122, we looked at a script to change the color of an object based on the position of the SL sun.The notable properties of the&hellip;<\/p>\n<p class=\"more-link-p\"><a class=\"more-link\" href=\"https:\/\/www.caledonoxbridge.org\/guardian\/2018\/08\/28\/lsl-scripting-whichever-way-the-wind-blows\/\">Read more &rarr;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[57],"tags":[58,59,60,61],"class_list":["post-486","post","type-post","status-publish","format-standard","hentry","category-scripting","tag-caledon-oxbridge","tag-cou","tag-scripting","tag-sl-skills"],"_links":{"self":[{"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/posts\/486","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/comments?post=486"}],"version-history":[{"count":13,"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/posts\/486\/revisions"}],"predecessor-version":[{"id":545,"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/posts\/486\/revisions\/545"}],"wp:attachment":[{"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/media?parent=486"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/categories?post=486"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.caledonoxbridge.org\/guardian\/wp-json\/wp\/v2\/tags?post=486"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}