Jump to content

Bugs Larnia

Resident
  • Posts

    84
  • Joined

  • Last visited

Reputation

69 Excellent

Retained

  • Member Title
    Innocent until proven innocent

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. 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;
  2. 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
  3. 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(); } }
  4. 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.
  5. 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 } } }
  6. * grabs a bucket of blue paint and heads for the rose garden *
  7. 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
  8. From the rotationally challenged among us: thank you!
  9. 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.
  10. 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.
  11. @Quistess Alpha mentioning you is quite akin telling Santa you've been good... you often find nice goodies show up after a while 😇
  12. 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.
  13. 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(); } }
  14. 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.
×
×
  • Create New...