-
Posts
86 -
Joined
-
Last visited
Content Type
Forums
Blogs
Knowledge Base
Everything posted by Bugs Larnia
-
Yes, should not be an issue. You will need to play with the timer interval and size increment a little to make it look fluent, and time the animation so that it starts expanding after you hug the ground (and in my head there is a sound playing going "Dun Dun DUUUUUNNNN!!" Also, is the chicken in the picture a suspect or a witness?
-
Hi MadsCook! The LL Wiki has a categorized library where you can find notecard readers, including configuration notecard readers which is what you are looking for. https://wiki.secondlife.com/wiki/CatNotecards Like Quistess said, I would not yet begin with llGetNotecardLineSync, since it has some caveats. Also, it is a new function and has not yet been rolled out to all sims at this time. Basically, you use the reader to store the keys in a list and use llListFindList to check against that. However, be sure to store them as keys using typecasting (i.e. prepend with "(key)"), since LSL is sometimes weird in comparing strings to keys when finding them in a list. Typecasting example: list lPersonnelKeys += (key)notecardLineData;
-
Building on this, you can also have the rezzer determine the derez time, so that you can, say, have a full plate of salmon last longer than a cup of espresso. default { on_rez(integer piParam) { llSetTimerEvent((float)piParam); // die after amount of seconds passed by the rezzer } timer() { llDie(); } } You would then need to pass the time in seconds into the llRezObject() function in the rezzer. You can make a function as below to simplify it a little. piRezTime is then the time in seconds that gets passed to the rezzed object in piParam parameter the on_rez(integer piParam) event. RezObject(string psDish, vector pvPos, rotation prRot, integer piRezTime) { llRezObject(psDish, pvPos, ZERO_VECTOR, prRot, piRezTime); } Edit: small documentation fix
-
Glow is set using llSetPrimitiveParams or llSetLinkPrimitiveParamsFast. If you pour it into a user defined function, you can simplify the call. I left out the alpha, but you can either add it or, more efficiently, implement it into the list of llSetPrimitiveParams rules. key gkOwner; integer giIsFlying; SetGlow(float pfGlowIntensity) { llSetPrimitiveParams([PRIM_GLOW, ALL_SIDES, pfGlowIntensity); } default { state_entry() { gkOwner = llGetOwner(); llSetTimerEvent(1.0); } changed(integer change) { if (change & (CHANGED_OWNER | CHANGED_INVENTORY)) llResetScript(); } timer() { integer ownerInfo = llGetAgentInfo(owner); if(ownerInfo & AGENT_IN_AIR) { if (!giIsFlying) { SetGlow(1.0); giIsFlying = TRUE; } } else { if (giIsFlying) { SetGlow(0.0); giIsFlying = FALSE; } } } on_rez(integer start_param) { llResetScript(); } }
-
Variables getting reset on inventory change
Bugs Larnia replied to FridayAfternoon's topic in LSL Scripting
This has been mentioned, but a few things can likely occur: Your global variable AnimationsArePlaying is set in the state_entry() or changed() events; Your StopAnimations() function is called in the state_entry() or changed() events; There is another function that changes AnimationsArePlaying, such as a function that collects the animations into the CurrentAnimations list I think the third option is the most likely, but it's hard to guess without seeing your changed(), or state_entry() code. -
Have one object talk with another object
Bugs Larnia replied to SEMaster Aftermath's topic in LSL Scripting
On little hack I have used (once, I think, but I can't recall for what or when) is using something innocuous like a color change of +/- 0.01 to trigger the changed() event in order to act as trigger for something else. In case of your movement, you can change the scale or color by e.g. 0.01, which will trigger the changed() event. If you change back and forth (-0.01, then +0.01), you can do this every time. See my demo script below. integer giSwitch; integer giFactor = 1; //Factor to switch between positive/negative adjustment TurnOn() { giSwitch = TRUE; llSetTimerEvent(3.0); } TurnOff() { giSwitch = FALSE; llSetTimerEvent(0.0); } default { state_entry() { TurnOff(); } touch_start(integer piNum) { if (giSwitch) { TurnOff(); } else { TurnOn(); } } timer() { vector gvMutation = <0.0, 0.01, 0.0> * giFactor; //Calculate the mutation llSetPos(llGetPos() + gvMutation); // Change the position (cannot be detected) llSetColor(llGetColor(ALL_SIDES) - gvMutation, ALL_SIDES); //Change the color minutely giFactor = -giFactor; //Switch the factor for the back and forth change } changed(integer piChange) { //Test whether the color was changed if (piChange & CHANGED_COLOR) { llOwnerSay((string)llGetPos()); //Send the data that cannot be detected } } } -
Prim Listens for chat commands to cause events of different duration
Bugs Larnia replied to Tirips's topic in LSL Scripting
The solution @Fenix Eldritch proposed (highlighted by @Love Zhaoying in their second suggestion) is the simplest and most elegant: by adding the llSetTimerEvent(0.0); in the timer() event, you will neatly shut down the timer. This should solve your issue -
Captain Kirk? That you?
-
From the rotationally challenged among us: thank you!
-
I understand the question you posed here was not what you wanted, but please don't remove the original questions. I might not be relevant to you (any more), but might be the exact answer someone else is looking for and now the answers no longer have context.
-
For question A: I often see two approaches being used: global variable or llGetPermissions. The global variable method sets a global variable (gasp!) to TRUE or FALSE. In your case: integer giAnimPerms; if (perm & PERMISSION_TRIGGER_ANIMATION) { giAnimPerms = TRUE; //Set to TRUE list anims = llGetAnimationList(llGetOwner()); if(llListFindList(anims, [llGetInventoryKey(animation)]) == -1) { llStartAnimation(animation); } } And in your timer if (amounta == amountb) { // Stop the timer. llSetTimerEvent(0.0); if (giAnimPerms) //Check perms { llStopAnimation(animation); giAnimPerms = FALSE; //Set to FALSE } llReleaseControls(); } The same can be done with llGetPermissions. In that case, you can leave the first section untouched, but you need to adjust the timer section: if (amounta == amountb) { // Stop the timer. llSetTimerEvent(0.0); integer iGrantedPermissions = llGetPermissions(); //Get the granted permissions if (iGrantedPermissions & PERMISSION_TRIGGER_ANIMATION) //Make sure you use a single & { llStopAnimation(animation); } llReleaseControls( ); } I'm not sure what the trade-off is, memory-wise, so I cannot tell you what the most efficient method is. As for question B: I'm not entirely understand your question, but I would take a look at the examples of https://wiki.secondlife.com/wiki/LlTakeControls. It's a pretty straightforward snippet on how to implement it.
-
@Quistess Alpha mentioning you is quite akin telling Santa you've been good... you often find nice goodies show up after a while 😇
-
This was something that was born out of laziness (that happens to me a lot 😏). Every time I had to write a script that did something like "get this prim's inventory and [insert action to take here]", I had to rewrite the inventory retrieval routine again. So, partly inspired by @Quistess Alpha (yes, again), I began creating a series of interface scripts: generic scripts that did tasks and sent the result though llMessageLinked. That way, I can just insert that script and focus my attention on what I want to do with the result (in case of this example: the aforementioned inventory list). Below is just an example of an interface script and a test implementation. I use an interface ID as a sort of "key", which is used by the implementing script to make sure the link_message event is raised by this particular script. // This is the interface ID: you can use this as a communication key to avoid clutter if you use multieple interface scripts integer GetInterfaceId() { return -55440986; } GenerateAndSendInventoryList() { SendInventoryList(GenerateInventoryList()); } list GenerateInventoryList() { list lInventory; integer iInventoryCount = llGetInventoryNumber(INVENTORY_ALL); if (iInventoryCount > 0) { integer i; for (i = 0; i < iInventoryCount; ++i) { string sName = llGetInventoryName(INVENTORY_ALL, i); //Since we can have multiple scripts in inventory, we can't just remove this script from the list if (llGetInventoryType(sName) != INVENTORY_SCRIPT) { lInventory += llGetInventoryName(INVENTORY_ALL, i); } } } return lInventory; } SendInventoryList(list plInventory) { llMessageLinked(LINK_SET, GetInterfaceId(), llDumpList2String(plInventory, "|"), NULL_KEY); } default { state_entry() { GenerateAndSendInventoryList(); } changed(integer piChange) { if (piChange & CHANGED_INVENTORY) { GenerateAndSendInventoryList(); } } on_rez(integer piParam) { llResetScript(); } } As you can see, this script updates the inventory on certain events and then sends it through llMessageLinked. So in and of itself, this script does nothing. The implementing script, in this case, might, for example, look something like this: list glInventory; //Use the same interface ID as the interface script integer GetInterfaceId() { return -55440986; } default { touch_start(integer total_number) { if (glInventory != []) { llGiveInventoryList(llDetectedKey(0), llGetObjectName(), glInventory); } else { llRegionSayTo(llDetectedKey(0), 0, "Bah! Humbug!"); } } link_message(integer piLinkNum, integer piNum, string psMsg, key pkId) { if (piNum == GetInterfaceId()) { glInventory = llParseString2List(psMsg, ["|"], []); } } } As you can see, I use the same key, encapsulated in GetInterfaceId(), to make sure that my signal comes from the right caller. Now all I have to do is write what I want to do with my inventory list. Mostly you'd want to give it to a person, but perhaps in another implementation, you want to transfer objects to another prim (like a HUD), or show a menu with these items. With these interface scripts, you can just add building blocks for repetitive tasks. This is just one very simple example of such a script to show you the principle behind it. Another example would be an on/off switch interface. What that does can be handled in an implementing script: a lamp, a scanner, movement, etc. I am sure others have a lot more of these, so feel free to add them in the comments.
-
This script will allow you to retrieve real world weather data for a location. Please note that this will require you to register at OpenWeatherMap.org (which is free and will allow a high number of free service calls per day) and obtain an API key, which will need to be pasted into gsApiKey for this to work. For readability, I have placed the city and country in global variables that are hard-coded, but you can, of course, expand this into something like a dialog or a menu. /* WEATHER CHECKER Created 2018-10-19 by Bugs Larnia ************* WARNING: This script requires that you register at OpenWeatherMap.org and append the API key to this script WARNING: DO NOT SHARE YOUR API KEY OR AN EDITABLE SCRIPT WITH YOUR API KEY IN IT!!! ************* Please keep annotations */ //USER SETTINGS string gsCity = "San Francisco"; //The city you want the weather for string gsCountry = "us"; //The country code (optional) string gsApiKey = "YOUR API_KEY HERE"; //The API key you receive from OpenWeatherMap.org //END USER SETTINGS string gsUrl = "https://api.openweathermap.org/data/2.5/weather?q="; //The base URL //Function to create the url + endpoint and send the request key SendCall() { string sUrl = gsUrl + llEscapeURL(gsCity) + "," + gsCountry + "&APPID=" + gsApiKey; key kRequest = llHTTPRequest(sUrl, [HTTP_METHOD, "GET", HTTP_BODY_MAXLENGTH, 16384], ""); return kRequest; } //Function to set/clear the floating text SetText(string psText) { if (psText != "") { psText += "\n \n \n \n"; } llSetText(psText, <1.0, 1.0, 0.0>, 1.0); } default { state_entry() { SetText(""); //Clear the text } touch_start(integer total_number) { SendCall(); //Send the request } http_response(key pkHttpReqId, integer piStatus, list plMetaData, string psBody) { if (piStatus == 200) //Check if the request is successful (200 == OK) { list lResponse = llJson2List(psBody); //Put the response in a list integer iIndex = llListFindList(lResponse, ["weather"]); //Get the weather item list lWeather = llJson2List(llList2String(lResponse, iIndex + 1)); //Parse the weather item data list lWeatherDetails = llJson2List(llList2String(lWeather, 0)); //Get the details integer iWeatherId = llList2Integer(lWeatherDetails, 1); //We need the weather ID //Morph the ID into a text //NOTE: A full list of weather ids can be found at: https://openweathermap.org/weather-conditions string sWeatherType = "having unknown weather"; if (iWeatherId < 300) { sWeatherType = "having thunderstorms"; } else if (iWeatherId < 400) { sWeatherType = "having some drizzle"; } else if (iWeatherId < 600) { sWeatherType = "having rain"; } else if (iWeatherId < 700) { sWeatherType = "having snow"; } else if (iWeatherId < 800) { sWeatherType = "under a bit of a haze"; } else if (iWeatherId == 800) { sWeatherType = "having a clear sky"; } else { sWeatherType = "having some clouds"; } //Set the floating text SetText(gsCity + " is " + sWeatherType + " at the moment."); } else //No 200, so error { SetText("Error " + (string)piStatus + "\n" + psBody); //Show the error } llSetTimerEvent(10.0); //Timer to clear the text } //Timer to clear the text timer() { llSetTimerEvent(0.0); SetText(""); } //Housekeeping on_rez(integer piParam) { llResetScript(); } }
-
code review HUD Script for Recording Position
Bugs Larnia replied to primerib1's topic in LSL Scripting
Honestly, I do like this idea of code reviewing. Maybe this is something that can be promoted, either on this forum, or a dedicated one. Code reviews are an amazing way to improve. -
code review HUD Script for Recording Position
Bugs Larnia replied to primerib1's topic in LSL Scripting
You use this pattern (which is not a bad pattern) a lot to communicate with the owner llRegionSayTo(gOwnerID, 0, "# Stopping recording..."); However, given the fact two arguments are constants and you use it a lot, putting it in a function would make the code better maintainable // Function SayToOwner(string psMsg) { llRegionSayTo(gOwnerID, 0, psMsg); } // Implementation SayToOwner("# Stopping recording..."); That said, I did spot a few instances of llOwnerSay as well. Why the difference? For consistency, I would choose one or the other unless you have a reason to use both (different text colors for example). Like Tessa said, I would put your listener channel numbers (e.g. -8008135) in a global variable. This way, should the channel ever change, you only have to do it once. For the listeners, I would also put the activation in a function, and since you use more than one channel, you can pass it as a parameter //Variables integer giMainChannel = -8008135; integer giSegmentChannel = -80081351; integer giRGBChannel = -80081352; integer giRecordingChannel = -80081353; //Function InitListener(integer piChannel) { gListener = llListen(piChannel, "", gOwnerID, ""); } //Call examples InitListener(giMainChannel); InitListener(giRGBChannel); ...etc... And, in the listen event you can then use an evaluation like if (channel == giMainChannel) { ... } Similarly for the dialog: ShowDialog() { llDialog(gOwnerID, "Choose command", gOtherCmds, giMainChannel); } The following statement does not require an else if (gRecording) state recording; else state default; You can rewrite that as if (gRecording) state recording; state default; The "else" is implicit here. The timer code can also be refactored: //Function HandleTimeOut() { llSetTimerEvent(0.0); llListenRemove(gListener); SayToOwner("# Timed out waiting for response"); //If you refactored as per my first comment } //Call timer() { HandleTimeOut(); state default; } Please note I have left the "state default" outside the function on purpose, as global functions are not allowed to change states (there is a work-around, but it's not pretty in this case). Hope it helps!! -
Limiting a Script to work only on a specific sim/estate
Bugs Larnia replied to Victoria Bennett's topic in LSL Scripting
They'd have to be determined for that script to work, but yes, for higher focus, you could use PARCEL_DETAILS_ID for parcel recognition. -
Assistance with Sim Restart Detector and Notifier
Bugs Larnia replied to Lilac Melodious's topic in LSL Scripting
What Quistess said. I am also noticing several calls to llSetTimerEvent(), but I can see no timer() event in any of your states. What are you trying to do there? -
From what I know, the difference between llPlaySound and llTriggerSound is that the former attaches the sound to the object and the other to the location of the object on the moment of triggering (i.e. if you move the object, the sound location stays as it was). You may want to look at llPreloadSound which preloads sounds in nearby viewers.
-
Scripting MOAP and looping query
Bugs Larnia replied to DogWomble Dollinger's topic in LSL Scripting
I think it might be this issue: SCR-112 - Using PARCEL_MEDIA_COMMAND_AGENT with llParcelMediaCommandList() when parcel's media URL is set causes previous video replay Can you check if the parcel's media url is set and if so, try clearing it? -
Script in object changing from mono to LSL?
Bugs Larnia replied to Profaitchikenz Haiku's topic in LSL Scripting
I didn't do it!! -
I used the portion of your script to replicate the issue, but it seemed to work as expected, so I am inclined to believe that your issue is in the "Do something else" part of your script. Does the "do something else" do anything with the timer, or, more likely, is it in a different state in the script that does not have a touch_start event?
-
@Quistess Alpha I tried this and when I selected a seated avatar, I got the root prim for the object they were sitting on. I used RC_GET_NORMAL instead of RC_GET_ROOT_KEY and it worked as expected. Now this is my first foray into llCastRay, so I am wondering if there was a specific reason you chose RC_GET_ROOT_KEY. I love the script though, and had a fun time puzzling with it. Thanks for sharing.