diff options
| author | David Walter Seikel | 2014-03-02 22:34:54 +1000 |
|---|---|---|
| committer | David Walter Seikel | 2014-03-02 22:34:54 +1000 |
| commit | 9c9a333860debf85b403a79bd6ee93b4f6bc5ded (patch) | |
| tree | a04fe89e2467782129dc6aba7b82d7e8cf57e97a /onefang's_utilities.lsl | |
| parent | Initial commit (diff) | |
| download | NPC-tool-9c9a333860debf85b403a79bd6ee93b4f6bc5ded.zip NPC-tool-9c9a333860debf85b403a79bd6ee93b4f6bc5ded.tar.gz NPC-tool-9c9a333860debf85b403a79bd6ee93b4f6bc5ded.tar.bz2 NPC-tool-9c9a333860debf85b403a79bd6ee93b4f6bc5ded.tar.xz | |
Initial import of everything.
Diffstat (limited to '')
| -rw-r--r-- | onefang's_utilities.lsl | 1189 |
1 files changed, 1189 insertions, 0 deletions
diff --git a/onefang's_utilities.lsl b/onefang's_utilities.lsl new file mode 100644 index 0000000..e088141 --- /dev/null +++ b/onefang's_utilities.lsl | |||
| @@ -0,0 +1,1189 @@ | |||
| 1 | // onefang's utilites version 3.0 | ||
| 2 | // Read a complete settings notecard and send settings to other scripts. | ||
| 3 | // Also other useful functions. | ||
| 4 | |||
| 5 | // Copyright (C) 2007 David Seikel (onefang rejected). | ||
| 6 | // | ||
| 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 8 | // of this software and associated documentation files (the "Software"), to | ||
| 9 | // deal in the Software without restriction, including without limitation the | ||
| 10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| 11 | // sell copies of the Software, and to permit persons to whom the Software is | ||
| 12 | // furnished to do so, subject to the following conditions: | ||
| 13 | // | ||
| 14 | // The above copyright notice and this permission notice shall be included in | ||
| 15 | // all copies of the Software and its Copyright notices. In addition publicly | ||
| 16 | // documented acknowledgment must be given that this software has been used if no | ||
| 17 | // source code of this software is made available publicly. This includes | ||
| 18 | // acknowledgments in either Copyright notices, Manuals, Publicity and Marketing | ||
| 19 | // documents or any documentation provided with any product containing this | ||
| 20 | // software. This License does not apply to any software that links to the | ||
| 21 | // libraries provided by this software (statically or dynamically), but only to | ||
| 22 | // the software provided. | ||
| 23 | // | ||
| 24 | // Please see the COPYING-PLAIN for a plain-english explanation of this notice | ||
| 25 | // and it's intent. | ||
| 26 | // | ||
| 27 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 28 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 29 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| 30 | // THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
| 31 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 32 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 33 | // | ||
| 34 | // As a special exception to the above conditions, the Second Life user known | ||
| 35 | // as Winter Ventura may ignore all permissions and conditions and provide her | ||
| 36 | // own. | ||
| 37 | |||
| 38 | // See the notecard "onefang's utilities manual". | ||
| 39 | |||
| 40 | |||
| 41 | float MENU_TIMEOUT = 45.0; | ||
| 42 | |||
| 43 | list channelHandles = []; // channel | listener | ||
| 44 | list chatChannels = []; // channel | script list | ||
| 45 | list chatOwners = []; // owner+channel | script list | ||
| 46 | list chatPrefixes = []; // channel+" "+prefix | script list (PREFIXES WITH SPACE) | ||
| 47 | list chatPrefixes2 = []; // channel+" "+prefix | script list (PREFIXES WITH NO SPACE) | ||
| 48 | list chatCommands = []; // script+channel | command list { command | argument defining stuff } | ||
| 49 | // TODO might be better to do - script+channel+" "+prefix | command list { command | argument defining stuff } | ||
| 50 | // and do away with the chatPrefix lists. Makes no space prefix checks harder I think. | ||
| 51 | // or add a prefix field to the list. | ||
| 52 | |||
| 53 | // SettingsReaderAndUtilities constants for copying into other code. | ||
| 54 | string LIST_SEP = "$!#"; // Used to seperate lists when sending them as strings. | ||
| 55 | integer UTILITIES_RESET = -1; | ||
| 56 | integer UTILITIES_RESET_DONE = -2; | ||
| 57 | integer UTILITIES_READ = -3; | ||
| 58 | integer UTILITIES_READ_DONE = -4; | ||
| 59 | integer UTILITIES_SUBSTITUTE = -5; | ||
| 60 | integer UTILITIES_SUBSTITUTE_DONE = -6; | ||
| 61 | integer UTILITIES_NEXT_WORD = -7; | ||
| 62 | integer UTILITIES_NEXT_WORD_DONE = -8; | ||
| 63 | integer UTILITIES_PAYMENT_MADE = -9; // Not used by this script | ||
| 64 | integer UTILITIES_PAYMENT_MADE_DONE = -10; // Not used by this script | ||
| 65 | integer UTILITIES_MENU = -11; | ||
| 66 | integer UTILITIES_MENU_DONE = -12; | ||
| 67 | integer UTILITIES_WARP = -13; | ||
| 68 | integer UTILITIES_WARP_DONE = -14; | ||
| 69 | integer UTILITIES_AVATAR_KEY = -15; // Redundant in OpenSim. | ||
| 70 | integer UTILITIES_AVATAR_KEY_DONE = -16; // Redundant in OpenSim. | ||
| 71 | integer UTILITIES_CHAT = -17; | ||
| 72 | integer UTILITIES_CHAT_DONE = -18; | ||
| 73 | integer UTILITIES_CHAT_FAKE = -19; | ||
| 74 | integer UTILITIES_CHAT_FAKE_DONE = -20; // Never used anywhere. | ||
| 75 | // TODO reuse -20 for CHAT_PURGE - which removes this scriptKey from all chat lists, usually before replacing them. | ||
| 76 | // Which was done some other way if I remember. | ||
| 77 | |||
| 78 | // internal constant and variable declarations. | ||
| 79 | integer KEYS_SCRIPT = 0; | ||
| 80 | integer KEYS_TYPE = 1; | ||
| 81 | integer KEYS_NAME = 2; | ||
| 82 | integer KEYS_EXTRA = 3; | ||
| 83 | integer KEYS_ID = 4; | ||
| 84 | integer KEYS_STRIDE = 5; | ||
| 85 | |||
| 86 | string REQUEST_KEY = "1"; | ||
| 87 | string REQUEST_IM = "2"; | ||
| 88 | string REQUEST_ONLINE = "3"; | ||
| 89 | string REQUEST_AVATAR = "4"; | ||
| 90 | |||
| 91 | integer MENU_USER = 0; // Key of user the big menu is for. | ||
| 92 | integer MENU_CHANNEL = 1; // Listen channel for big menu dialogs. | ||
| 93 | integer MENU_SCRIPT = 2; // ScriptKey of the script calling the menu. | ||
| 94 | integer MENU_HANDLE = 3; // Listen ID for big menu dialogs. | ||
| 95 | integer MENU_TYPE = 4; // Type of menu. | ||
| 96 | integer MENU_MESSAGE = 5; // Message for the top of big menu dialog. | ||
| 97 | integer MENU_MENU = 6; // The big menu itself. LIST_SEP separated when they come in. | ||
| 98 | integer MENU_FILTER = 7; // Regex filter for inventory menus. | separated when they come in. | ||
| 99 | integer MENU_POS = 8; // Current position within big menu. | ||
| 100 | integer MENU_MAXPOS = 9; // Maximum position within big menu. | ||
| 101 | integer MENU_NAMES = 10; // Long names to get around stupid SL menu limitations. | ||
| 102 | integer MENU_TIME = 11; // Otherwise this list is ever growing. Damn Ignore button. sigh | ||
| 103 | integer MENU_STRIDE = 12; | ||
| 104 | |||
| 105 | list settingsCards = []; // Queue of cards to read. | ||
| 106 | string settingsName = ".settings"; // Name of a notecard in the object's inventory. | ||
| 107 | key settingsKey = NULL_KEY; // ScriptKey of the script reading the card. | ||
| 108 | integer settingsLine = 0; // Current line number. | ||
| 109 | key settingsQueryID = NULL_KEY; // ID used to identify dataserver queries. | ||
| 110 | |||
| 111 | list menus = []; // The menus. | ||
| 112 | list registeredMenus = []; // Any scripts that have registered as needing a touch menu. | ||
| 113 | list keyRequests = []; // A list of avatar key requests. | ||
| 114 | |||
| 115 | list ANIMATIONS = | ||
| 116 | [ | ||
| 117 | "aim_L_bow", "aim_R_bazooka", "aim_R_handgun", "aim_R_rifle", "angry_fingerwag", | ||
| 118 | "angry_tantrum", "away", "backflip", "blowkiss", "bow", "brush", "clap", | ||
| 119 | "courtbow", "cross_arms", "crouch", "crouchwalk", "curtsy", | ||
| 120 | "dance1", "dance2", "dance3", "dance4", "dance5", "dance6", "dance7", "dance8", | ||
| 121 | "dead", "drink", "express_afraid", "express_anger", "express_bored", | ||
| 122 | "express_cry", "express_embarrased", "express_laugh", "express_repulsed", | ||
| 123 | "express_sad", "express_shrug", "express_surprise", "express_wink", | ||
| 124 | "express_worry", "falldown", "female_walk", "fist_pump", "fly", "flyslow", | ||
| 125 | "hello", "hold_R_bow", "hold_R_bazooka", "hold_R_handgun", "hold_R_rifle", | ||
| 126 | "hold_throw_R", "hover", "hover_down", "hover_up", "impatient", | ||
| 127 | "jump", "jumpforjoy", "kick_roundhouse_R", "kissmybutt", "kneel_left", | ||
| 128 | "kneel_right", "land", "laugh_short", "motorcycle_sit", "musclebeach", "no_head", | ||
| 129 | "no_unhappy", "nyanya", "peace", "point_me", "point_you", | ||
| 130 | "prejump", "punch_L", "punch_onetwo", "punch_R", | ||
| 131 | "RPS_countdown", "RPS_paper", "RPS_rock", "RPS_scissors", | ||
| 132 | "run", "salute", "shoot_L_bow", "shout", "sit", "sit_female", "sit_ground", | ||
| 133 | "sit_to_stand", "sleep", "slowwalk", "smoke_idle", "smoke_inhale", | ||
| 134 | "smoke_throw_down", "snapshot", "soft_land", "stand", "standup", "stand_1", | ||
| 135 | "stand_2", "stand_3", "stand_4", "stretch", "stride", "surf", "sword_strike_R", | ||
| 136 | "talk", "throw_R", "tryon_shirt", "turnback_180", "turnleft", "turnright", | ||
| 137 | "turn_180", "type", "uphillwalk", "walk", "whisper", "whistle", "wink_hollywood", | ||
| 138 | "yell", "yes_happy", "yes_head", "yoga_float" | ||
| 139 | ]; | ||
| 140 | |||
| 141 | |||
| 142 | list addChatScripts(list thisList, string name, string value, integer stride) | ||
| 143 | { | ||
| 144 | integer found = llListFindList(thisList, [name]); | ||
| 145 | |||
| 146 | if (0 <= found) | ||
| 147 | { | ||
| 148 | list values = llParseString2List(llList2String(thisList, found + 1), ["|"], []); | ||
| 149 | |||
| 150 | if (1 == stride) | ||
| 151 | { | ||
| 152 | integer subFound = llListFindList(values, [value]); | ||
| 153 | |||
| 154 | if (0 > subFound) | ||
| 155 | values += [value]; | ||
| 156 | } | ||
| 157 | else | ||
| 158 | { | ||
| 159 | integer length = llGetListLength(values); | ||
| 160 | integer i; | ||
| 161 | |||
| 162 | for (i = 0; i < length; i += stride) | ||
| 163 | { | ||
| 164 | list sub = llList2List(values, i, i + stride - 1); | ||
| 165 | integer subFound = llListFindList(values, [llList2String(sub, 0)]); | ||
| 166 | |||
| 167 | if (0 > subFound) | ||
| 168 | values += sub; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | thisList = llListReplaceList(thisList, [name, llDumpList2String(values, "|")], found, found + 1); | ||
| 172 | } | ||
| 173 | else | ||
| 174 | thisList += [name, value]; | ||
| 175 | return thisList; | ||
| 176 | } | ||
| 177 | |||
| 178 | list delChatScripts(list thisList, string name, string value, integer stride) | ||
| 179 | { | ||
| 180 | integer found = llListFindList(thisList, [name]); | ||
| 181 | |||
| 182 | if (0 <= found) | ||
| 183 | { | ||
| 184 | list values = llParseString2List(llList2String(thisList, found + 1), ["|"], []); | ||
| 185 | |||
| 186 | if (1 == stride) | ||
| 187 | { | ||
| 188 | integer subFound = llListFindList(values, [value]); | ||
| 189 | |||
| 190 | if (0 <= subFound) | ||
| 191 | values = llDeleteSubList(values, subFound, subFound); | ||
| 192 | } | ||
| 193 | else | ||
| 194 | { | ||
| 195 | integer length = llGetListLength(values); | ||
| 196 | integer i; | ||
| 197 | |||
| 198 | for (i = 0; i < length; i += stride) | ||
| 199 | { | ||
| 200 | list sub = llList2List(values, i, i + stride - 1); | ||
| 201 | integer subFound = llListFindList(values, [llList2String(sub, 0)]); | ||
| 202 | |||
| 203 | if (0 <= subFound) | ||
| 204 | values = llDeleteSubList(values, subFound, subFound + stride - 1); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | if (llGetListLength(values)) | ||
| 208 | thisList = llListReplaceList(thisList, [name, llDumpList2String(values, "|")], found, found + 1); | ||
| 209 | else | ||
| 210 | thisList = llDeleteSubList(thisList, found, found + 1); | ||
| 211 | } | ||
| 212 | return thisList; | ||
| 213 | } | ||
| 214 | |||
| 215 | resetPrimShit() | ||
| 216 | { | ||
| 217 | llParticleSystem([]); | ||
| 218 | // Comment this out for now. Causes problems with updates, and is probably not needed. | ||
| 219 | // llSetRemoteScriptAccessPin(0); | ||
| 220 | llSetText("", <1, 1, 1 >, 1.0); | ||
| 221 | llSetTextureAnim(FALSE | SMOOTH | LOOP, ALL_SIDES, 1, 1, 0, 0, 0.0); | ||
| 222 | llTargetOmega(<0, 0, 0 >, 0, 0); | ||
| 223 | } | ||
| 224 | |||
| 225 | // Get the next word. | ||
| 226 | // The last parameter is the last word returned from the previous call. | ||
| 227 | // The list results are - | ||
| 228 | // 0 = The rest of the text. | ||
| 229 | // 1 = The next word. | ||
| 230 | list nextWord(string separator, string message, string last) | ||
| 231 | { | ||
| 232 | list result = []; | ||
| 233 | integer index; | ||
| 234 | |||
| 235 | index = llSubStringIndex(message, separator); | ||
| 236 | if (0 <= index) | ||
| 237 | { | ||
| 238 | if (0 != index) // Check for a corner case. | ||
| 239 | last += llGetSubString(message, 0, index - 1); | ||
| 240 | if ((index + 1) < llStringLength(message)) | ||
| 241 | message = llGetSubString(message, index + 1, -1); | ||
| 242 | else | ||
| 243 | message = ""; | ||
| 244 | result += message; | ||
| 245 | result += last; | ||
| 246 | } | ||
| 247 | else | ||
| 248 | { | ||
| 249 | result += ""; | ||
| 250 | result += last + message; | ||
| 251 | } | ||
| 252 | |||
| 253 | return (result); | ||
| 254 | } | ||
| 255 | |||
| 256 | // Substitute params from a list. | ||
| 257 | string substitute(list values, string separator) | ||
| 258 | { | ||
| 259 | string result = ""; | ||
| 260 | string original = llList2String(values, 0); | ||
| 261 | integer length = llGetListLength(values); | ||
| 262 | |||
| 263 | // integer length = (values != []); // Speed hack. | ||
| 264 | integer index = 1; | ||
| 265 | |||
| 266 | while ((0 <= index) && ("" != original)) | ||
| 267 | { | ||
| 268 | index = llSubStringIndex(original, separator); | ||
| 269 | if (0 <= index) | ||
| 270 | { | ||
| 271 | string last = separator; | ||
| 272 | integer i; | ||
| 273 | |||
| 274 | if (0 != index) // Check for a corner case. | ||
| 275 | result += llGetSubString(original, 0, index - 1); | ||
| 276 | if ((index + 2) < llStringLength(original)) | ||
| 277 | { | ||
| 278 | last = llGetSubString(original, index + 1, index + 1); | ||
| 279 | original = llGetSubString(original, index + 2, -1); | ||
| 280 | } | ||
| 281 | else | ||
| 282 | original = ""; | ||
| 283 | |||
| 284 | for (i = 1; i < length; ++i) | ||
| 285 | { | ||
| 286 | string pattern = llList2String(values, i); | ||
| 287 | |||
| 288 | if (llGetSubString(pattern, 0, 0) == last) | ||
| 289 | { | ||
| 290 | last = llGetSubString(pattern, 1, -1); | ||
| 291 | i = length; // A break statement would be nice. | ||
| 292 | } | ||
| 293 | } | ||
| 294 | result += last; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | return result + original; | ||
| 299 | } | ||
| 300 | |||
| 301 | startMenu(key id, list input) | ||
| 302 | { | ||
| 303 | key menuUser = NULL_KEY; | ||
| 304 | integer menuChannel = -1; | ||
| 305 | key menuScript = NULL_KEY; | ||
| 306 | integer menuHandle = 0; | ||
| 307 | integer menuType = INVENTORY_NONE; | ||
| 308 | string menuMessage = ""; | ||
| 309 | list menu = []; | ||
| 310 | list menuFilter = []; | ||
| 311 | integer menuPos = 0; | ||
| 312 | integer menuMaxPos = 1; | ||
| 313 | // list menuNames = []; | ||
| 314 | integer menuIndex = llGetListLength(menus); | ||
| 315 | |||
| 316 | menuPos = 0; | ||
| 317 | menuScript = id; | ||
| 318 | menuUser = llList2Key(input, 0); | ||
| 319 | menuFilter = llParseStringKeepNulls(llList2String(input, 1), ["|"], []); | ||
| 320 | menuType = llList2Integer(menuFilter, 0); | ||
| 321 | if (llGetListLength(menuFilter) > 1) | ||
| 322 | menuFilter = llList2List(menuFilter, 1, -1); | ||
| 323 | else | ||
| 324 | menuFilter = []; | ||
| 325 | menuMessage = llList2String(input, 2); | ||
| 326 | menu = llList2List(input, 3, -1); | ||
| 327 | if (NULL_KEY == menuUser) | ||
| 328 | { | ||
| 329 | // Only add it if it's not there. | ||
| 330 | if (-1 == llListFindList(registeredMenus, [menuMessage + "|" + menuScript])) | ||
| 331 | registeredMenus += [menuMessage + "|" + menuScript]; | ||
| 332 | registeredMenus = llListSort(registeredMenus, 1, TRUE); | ||
| 333 | return; | ||
| 334 | } | ||
| 335 | else if (INVENTORY_NONE == menuType) | ||
| 336 | menuMaxPos = llGetListLength(menu); | ||
| 337 | else if (INVENTORY_ALL == menuType) // TODO - NONE and ALL are both -1. lol | ||
| 338 | menuMaxPos = llGetListLength(ANIMATIONS); | ||
| 339 | else | ||
| 340 | { | ||
| 341 | integer i; | ||
| 342 | integer j; | ||
| 343 | integer number = llGetInventoryNumber(menuType); // NOTE this may change while we are in the menus. | ||
| 344 | integer length = llGetListLength(menuFilter); | ||
| 345 | |||
| 346 | menuMaxPos = 0; | ||
| 347 | menu= []; | ||
| 348 | for (i = 0; i < number; ++i) | ||
| 349 | { | ||
| 350 | string name = llGetInventoryName(menuType, i); | ||
| 351 | |||
| 352 | if (length) | ||
| 353 | { | ||
| 354 | for (j = 0; j < length; ++j) | ||
| 355 | { | ||
| 356 | if (osRegexIsMatch(name, llList2String(menuFilter, j))) | ||
| 357 | { | ||
| 358 | ++menuMaxPos; | ||
| 359 | menu += name; | ||
| 360 | } | ||
| 361 | } | ||
| 362 | } | ||
| 363 | else | ||
| 364 | { | ||
| 365 | ++menuMaxPos; | ||
| 366 | menu += name; | ||
| 367 | } | ||
| 368 | } | ||
| 369 | } | ||
| 370 | menuChannel = (integer) (llFrand(10000) + 1000); | ||
| 371 | menuHandle = llListen(menuChannel, "", menuUser, ""); | ||
| 372 | menuMaxPos /= 9; | ||
| 373 | menuMaxPos *= 9; | ||
| 374 | ++menuMaxPos; | ||
| 375 | menus += [menuUser, menuChannel, menuScript, menuHandle, menuType, menuMessage, | ||
| 376 | llDumpList2String(menu, LIST_SEP), llDumpList2String(menuFilter, "|"), | ||
| 377 | menuPos, menuMaxPos, "", llGetTime()]; | ||
| 378 | showMenu(menuIndex); | ||
| 379 | } | ||
| 380 | |||
| 381 | showMenu(integer menuIndex) | ||
| 382 | { | ||
| 383 | key menuUser = llList2String (menus, menuIndex + MENU_USER); | ||
| 384 | integer menuChannel = llList2Integer(menus, menuIndex + MENU_CHANNEL); | ||
| 385 | key menuScript = llList2String (menus, menuIndex + MENU_SCRIPT); | ||
| 386 | integer menuHandle = llList2Integer(menus, menuIndex + MENU_HANDLE); | ||
| 387 | integer menuType = llList2Integer(menus, menuIndex + MENU_TYPE); | ||
| 388 | string menuMessage = llList2String (menus, menuIndex + MENU_MESSAGE); | ||
| 389 | list menu = llParseString2List(llList2String (menus, menuIndex + MENU_MENU), [LIST_SEP], []); | ||
| 390 | list menuFilter = llParseString2List(llList2String (menus, menuIndex + MENU_FILTER), ["|"], []); | ||
| 391 | integer menuPos = llList2Integer(menus, menuIndex + MENU_POS); | ||
| 392 | integer menuMaxPos = llList2Integer(menus, menuIndex + MENU_MAXPOS); | ||
| 393 | list menuNames = []; | ||
| 394 | |||
| 395 | list thisMenu = []; | ||
| 396 | integer length; | ||
| 397 | integer i; | ||
| 398 | |||
| 399 | for (i = 0; i < 12; ++i) | ||
| 400 | { | ||
| 401 | string name; | ||
| 402 | integer index; | ||
| 403 | |||
| 404 | if (INVENTORY_NONE == menuType) | ||
| 405 | name = llList2String(menu, menuPos + i); | ||
| 406 | else if (INVENTORY_ALL == menuType) | ||
| 407 | name = llList2String(ANIMATIONS, menuPos + i); | ||
| 408 | else | ||
| 409 | name = llList2String(menu, menuPos + i); | ||
| 410 | // name = llGetInventoryName(menuType, menuPos + i); | ||
| 411 | |||
| 412 | index = llSubStringIndex(name, "|"); | ||
| 413 | if (index != -1) | ||
| 414 | { | ||
| 415 | list parts = llParseStringKeepNulls(name, ["|"], []); | ||
| 416 | name = llGetSubString(llList2String(parts, 0) + " ", 0, 15) | ||
| 417 | + "|" + llList2String(parts, 1)+ "|" + llList2String(parts, 2); | ||
| 418 | } | ||
| 419 | if (llStringLength(name) > 24) | ||
| 420 | { | ||
| 421 | menuNames += [name]; | ||
| 422 | name = llGetSubString(name, 0, 23); | ||
| 423 | } | ||
| 424 | // TODO - Only allow blank ones for arbitrary menus, but screws with the showMenu() code. | ||
| 425 | // if ((INVENTORY_NONE == menuType) && ("" == name)) | ||
| 426 | // name = "."; | ||
| 427 | if ("" != name) | ||
| 428 | thisMenu += [name]; | ||
| 429 | } | ||
| 430 | |||
| 431 | length = llGetListLength(thisMenu); | ||
| 432 | if ((12 > length) && (0 == menuPos)) | ||
| 433 | { | ||
| 434 | integer j = length % 3; | ||
| 435 | if (0 == j) | ||
| 436 | thisMenu += [".", "Exit", "."]; | ||
| 437 | else if (1 == j) | ||
| 438 | { | ||
| 439 | string last = llList2String(thisMenu, -1); | ||
| 440 | thisMenu = llDeleteSubList(thisMenu, -1, -1) + [last, "Exit", "."]; | ||
| 441 | } | ||
| 442 | else if (2 == j) | ||
| 443 | { | ||
| 444 | string penultimate = llList2String(thisMenu, -2); | ||
| 445 | string last = llList2String(thisMenu, -1); | ||
| 446 | thisMenu = llDeleteSubList(thisMenu, -2, -1) + [penultimate, "Exit", last]; | ||
| 447 | } | ||
| 448 | } | ||
| 449 | else if (9 >= length) | ||
| 450 | thisMenu += ["<<", "Exit", ">>"]; | ||
| 451 | else | ||
| 452 | thisMenu = llList2List(thisMenu, 0, 8) + ["<<", "Exit", ">>"]; | ||
| 453 | |||
| 454 | // Re order them to make LSL happy. | ||
| 455 | for (i = 0; i < length; i += 3) | ||
| 456 | thisMenu = llListInsertList(llDeleteSubList(thisMenu, -3, -1), llList2List(thisMenu, -3, -1), i); | ||
| 457 | llDialog(menuUser, menuMessage, thisMenu, menuChannel); | ||
| 458 | menus = llListReplaceList(menus, | ||
| 459 | [ | ||
| 460 | menuUser, menuChannel, menuScript, menuHandle, menuType, menuMessage, | ||
| 461 | llDumpList2String(menu, LIST_SEP), llDumpList2String(menuFilter, "|"), | ||
| 462 | menuPos, menuMaxPos, llDumpList2String(menuNames, LIST_SEP), llGetTime() | ||
| 463 | ], menuIndex, menuIndex + MENU_STRIDE - 1); | ||
| 464 | } | ||
| 465 | |||
| 466 | myListen(integer channel, string name, key id, string message) | ||
| 467 | { | ||
| 468 | // check if this message came from an object or avatar, | ||
| 469 | // use the objects owner if it came from an object. | ||
| 470 | // Avatars own themselves. B-) | ||
| 471 | key realId = llGetOwnerKey(id); | ||
| 472 | integer menuIndex = llListFindList(menus, [realId, channel]); | ||
| 473 | |||
| 474 | if (0 <= menuIndex) // Menu response | ||
| 475 | { | ||
| 476 | key menuUser = llList2String (menus, menuIndex + MENU_USER); | ||
| 477 | integer menuChannel = llList2Integer(menus, menuIndex + MENU_CHANNEL); | ||
| 478 | key menuScript = llList2String (menus, menuIndex + MENU_SCRIPT); | ||
| 479 | integer menuHandle = llList2Integer(menus, menuIndex + MENU_HANDLE); | ||
| 480 | integer menuType = llList2Integer(menus, menuIndex + MENU_TYPE); | ||
| 481 | string menuMessage = llList2String (menus, menuIndex + MENU_MESSAGE); | ||
| 482 | list menu = llParseString2List(llList2String (menus, menuIndex + MENU_MENU), [LIST_SEP], []); | ||
| 483 | list menuFilter = llParseString2List(llList2String (menus, menuIndex + MENU_FILTER), ["|"], []); | ||
| 484 | integer menuPos = llList2Integer(menus, menuIndex + MENU_POS); | ||
| 485 | integer menuMaxPos = llList2Integer(menus, menuIndex + MENU_MAXPOS); | ||
| 486 | list menuNames = llParseString2List(llList2String (menus, menuIndex + MENU_NAMES), [LIST_SEP], []); | ||
| 487 | integer delete = FALSE; | ||
| 488 | |||
| 489 | if ("<<" == message) | ||
| 490 | { | ||
| 491 | menuPos -= 9; | ||
| 492 | if (menuPos < 0) | ||
| 493 | menuPos = menuMaxPos - 1; | ||
| 494 | } | ||
| 495 | else if (">>" == message) | ||
| 496 | { | ||
| 497 | menuPos += 9; | ||
| 498 | if (menuPos > (menuMaxPos - 1)) | ||
| 499 | menuPos = 0; | ||
| 500 | } | ||
| 501 | else if ("." == message) | ||
| 502 | delete = TRUE; | ||
| 503 | else | ||
| 504 | { | ||
| 505 | delete = TRUE; | ||
| 506 | if (menuHandle) | ||
| 507 | llListenRemove(menuHandle); | ||
| 508 | if (llStringLength(message) == 24) | ||
| 509 | { | ||
| 510 | integer i; | ||
| 511 | integer length = llGetListLength(menuNames); | ||
| 512 | |||
| 513 | for (i = 0; i < length; ++i) | ||
| 514 | { | ||
| 515 | string lName = llList2String(menuNames, i); | ||
| 516 | |||
| 517 | if (message == llGetSubString(lName, 0, 23)) | ||
| 518 | message = lName; | ||
| 519 | } | ||
| 520 | } | ||
| 521 | |||
| 522 | if (NULL_KEY == menuScript) | ||
| 523 | { | ||
| 524 | menuScript = (key) llList2String(llParseStringKeepNulls(message, ["|"], []), 2); | ||
| 525 | message = ""; | ||
| 526 | } | ||
| 527 | llMessageLinked(LINK_SET, UTILITIES_MENU_DONE, llDumpList2String([menuUser, message], LIST_SEP), menuScript); | ||
| 528 | } | ||
| 529 | if (delete) | ||
| 530 | menus = llDeleteSubList(menus, menuIndex, menuIndex + MENU_STRIDE - 1); | ||
| 531 | else | ||
| 532 | { | ||
| 533 | menus = llListReplaceList(menus, | ||
| 534 | [ | ||
| 535 | menuUser, menuChannel, menuScript, menuHandle, menuType, menuMessage, | ||
| 536 | llDumpList2String(menu, LIST_SEP), llDumpList2String(menuFilter, "|"), | ||
| 537 | menuPos, menuMaxPos, llDumpList2String(menuNames, LIST_SEP), llGetTime() | ||
| 538 | ], menuIndex, menuIndex + MENU_STRIDE - 1); | ||
| 539 | showMenu(menuIndex); | ||
| 540 | } | ||
| 541 | } | ||
| 542 | else // Chat command. | ||
| 543 | { | ||
| 544 | list scripts = []; | ||
| 545 | list words = []; | ||
| 546 | string prefix = ""; | ||
| 547 | string command = ""; | ||
| 548 | string idChannel = (string) realId + (string) channel; | ||
| 549 | integer thisOwner = llListFindList(chatOwners, [idChannel]); | ||
| 550 | |||
| 551 | //llOwnerSay("->>" + (string) channel + " " + message); | ||
| 552 | |||
| 553 | //llOwnerSay("owners " + llDumpList2String(chatOwners, "^")); | ||
| 554 | //llOwnerSay("channels " + llDumpList2String(chatChannels, "^")); | ||
| 555 | //llOwnerSay("prefixes " + llDumpList2String(chatPrefixes, "^")); | ||
| 556 | //llOwnerSay("prefixes2 " + llDumpList2String(chatPrefixes2, "^")); | ||
| 557 | //llOwnerSay("commands " + llDumpList2String(chatCommands, "^")); | ||
| 558 | if (0 <= thisOwner) | ||
| 559 | scripts = llParseString2List(llList2String(chatOwners, thisOwner + 1), ["|"], []); | ||
| 560 | else | ||
| 561 | { | ||
| 562 | integer thisChannel = llListFindList(chatChannels, [(string) channel]); | ||
| 563 | |||
| 564 | if (0 <= thisChannel) | ||
| 565 | scripts = llParseString2List(llList2String(chatChannels, thisChannel + 1), ["|"], []); | ||
| 566 | } | ||
| 567 | //llOwnerSay(llDumpList2String(scripts, "|")); | ||
| 568 | if ([] != scripts) | ||
| 569 | { | ||
| 570 | integer thisPrefix; | ||
| 571 | string candidate; | ||
| 572 | |||
| 573 | words = llParseString2List(message, [" "], []); | ||
| 574 | candidate = llList2String(words, 0); | ||
| 575 | thisPrefix = llListFindList(chatPrefixes, [(string) channel + " " + candidate]); | ||
| 576 | //llOwnerSay(llDumpList2String(words, "~")); | ||
| 577 | //llSay(0, candidate); | ||
| 578 | //llSay(0, (string) thisPrefix); | ||
| 579 | if (0 <= thisPrefix) | ||
| 580 | { | ||
| 581 | prefix = candidate; | ||
| 582 | scripts = llParseString2List(llList2String(chatPrefixes, thisPrefix + 1), ["|"], []); | ||
| 583 | words = llList2List(words, 1, -1); | ||
| 584 | } | ||
| 585 | else | ||
| 586 | { | ||
| 587 | integer length = llGetListLength(chatPrefixes2); | ||
| 588 | integer i; | ||
| 589 | |||
| 590 | for (i = 0; i < length; i += 2) | ||
| 591 | { | ||
| 592 | string pName = llList2String(chatPrefixes2, i); | ||
| 593 | integer pLength = llStringLength(pName); | ||
| 594 | |||
| 595 | if (pName == ((string) channel + " " + llGetSubString(candidate, 0, pLength))) | ||
| 596 | { | ||
| 597 | // prefix = pName; | ||
| 598 | prefix = candidate; | ||
| 599 | scripts = llParseString2List(llList2String(chatPrefixes2, i + 1), ["|"], []); | ||
| 600 | // words = [llGetSubString(candidate, pLength + 1, -1)] + llList2List(words, 1, -1); | ||
| 601 | words = llList2List(words, 1, -1); | ||
| 602 | i = length; | ||
| 603 | } | ||
| 604 | } | ||
| 605 | } | ||
| 606 | } | ||
| 607 | //llOwnerSay(llDumpList2String(scripts, "|")); | ||
| 608 | //llSay(0, prefix); | ||
| 609 | |||
| 610 | // Finally found it, process it. | ||
| 611 | if ([] != scripts) | ||
| 612 | { | ||
| 613 | integer length = llGetListLength(scripts); | ||
| 614 | integer wordLength = llGetListLength(words); | ||
| 615 | integer i; | ||
| 616 | |||
| 617 | // Wont put up with laggy scripts. Use a prefix if you insist on using local chat. | ||
| 618 | // if ((0 == channel) && ("" == prefix)) | ||
| 619 | // return; | ||
| 620 | |||
| 621 | // The problem with this loop is that if a bunch of scripts are wanting the same commands, | ||
| 622 | // then it's not efficient. In the expected use cases, that should not me much of a problem. | ||
| 623 | for (i = 0; i < length; ++i) | ||
| 624 | { | ||
| 625 | string script = llList2String(scripts, i); | ||
| 626 | integer theseCommands = llListFindList(chatCommands, [script + (string) channel]); | ||
| 627 | |||
| 628 | if (0 <= theseCommands) | ||
| 629 | { | ||
| 630 | list commands = llParseStringKeepNulls(llList2String(chatCommands, theseCommands + 1), ["|"], []); | ||
| 631 | integer thisCommand = llListFindList(commands, [llList2String(words, 0)]); | ||
| 632 | //llOwnerSay(llDumpList2String(words, "~")); | ||
| 633 | //llOwnerSay(llDumpList2String(commands, "~")); | ||
| 634 | //llSay(0, (string) thisCommand); | ||
| 635 | |||
| 636 | if (0 != (thisCommand % 2)) | ||
| 637 | thisCommand = -1; | ||
| 638 | if (0 <= thisCommand) | ||
| 639 | { | ||
| 640 | list result = [channel, name, realId, message, prefix, llList2String(commands, thisCommand)]; | ||
| 641 | string argsTypes = llList2String(commands, thisCommand + 1); | ||
| 642 | integer argsLength = llStringLength(argsTypes); | ||
| 643 | integer required = 0; | ||
| 644 | integer optional = 0; | ||
| 645 | integer multiple = -1; | ||
| 646 | // integer oldStyle = FALSE; | ||
| 647 | |||
| 648 | if (0 < argsLength) // Arguments expected. | ||
| 649 | { | ||
| 650 | required = (integer) llGetSubString(argsTypes, 0, 0); | ||
| 651 | optional = (integer) llGetSubString(argsTypes, 1, 1); | ||
| 652 | |||
| 653 | { | ||
| 654 | list arguments = []; // type, required, extra | ||
| 655 | integer a; | ||
| 656 | integer argsCount = 0; | ||
| 657 | integer w = 1; | ||
| 658 | |||
| 659 | for (a = 0; a < argsLength; a++) | ||
| 660 | { | ||
| 661 | string type = llGetSubString(argsTypes, a, a); | ||
| 662 | string TYPE = llToUpper(type); | ||
| 663 | integer isNeeded = (TYPE == type); | ||
| 664 | string extra = ""; | ||
| 665 | |||
| 666 | if (isNeeded) | ||
| 667 | ++required; | ||
| 668 | else | ||
| 669 | ++optional; | ||
| 670 | // Sort out the extra string for those that support it. | ||
| 671 | if (("C" == TYPE) || ("X" == TYPE)) | ||
| 672 | { | ||
| 673 | string subbie = llGetSubString(argsTypes, a + 1, -1); | ||
| 674 | integer comma = llSubStringIndex(subbie, ","); | ||
| 675 | |||
| 676 | if (-1 == comma) | ||
| 677 | { | ||
| 678 | extra = llGetSubString(argsTypes, a + 1, -1); | ||
| 679 | a = argsLength; | ||
| 680 | } | ||
| 681 | else | ||
| 682 | { | ||
| 683 | extra = llGetSubString(argsTypes, a + 1, a + 1 + comma - 1); | ||
| 684 | a += comma + 1; | ||
| 685 | } | ||
| 686 | } | ||
| 687 | arguments += [TYPE, isNeeded, extra]; | ||
| 688 | ++argsCount; | ||
| 689 | } | ||
| 690 | for (a = 0; a < argsCount; ++a) | ||
| 691 | { | ||
| 692 | string TYPE = llList2String (arguments, (a * 3) + 0); | ||
| 693 | integer isNeeded = llList2Integer(arguments, (a * 3) + 1); | ||
| 694 | string extra = llList2String (arguments, (a * 3) + 2); | ||
| 695 | string value = ""; | ||
| 696 | |||
| 697 | // a animation, b bodypart, clothing, g gesture, m landmark, o object, sound, t texture. | ||
| 698 | // c notecard, x script, Extension is what follows up to the next comma, or end of line. | ||
| 699 | // f float, i integer, k key, r rotation, s rest of string, v vector. | ||
| 700 | // n name of avatar or NPC. Full two word variety, no other checking done. | ||
| 701 | // l local name, could be just first or last name, but they need to be in the sim. | ||
| 702 | if ("F" == TYPE) | ||
| 703 | value = (string) llList2Float(words, w); | ||
| 704 | else if ("I" == TYPE) | ||
| 705 | value = (string) llList2Integer(words, w); | ||
| 706 | else if ("K" == TYPE) | ||
| 707 | value = (key) llList2String(words, w); | ||
| 708 | else if (("R" == TYPE) || ("V" == TYPE)) | ||
| 709 | { | ||
| 710 | string next = llList2String(words, w); | ||
| 711 | |||
| 712 | if ("<" == llGetSubString(next, 0, 0)) | ||
| 713 | { | ||
| 714 | integer l; | ||
| 715 | |||
| 716 | for (l = 0; (w + l) < wordLength; ++l) | ||
| 717 | { | ||
| 718 | value += next; | ||
| 719 | if (">" == llGetSubString(value, -1, -1)) | ||
| 720 | { | ||
| 721 | w += l; | ||
| 722 | l = wordLength; // BREAK! | ||
| 723 | } | ||
| 724 | if (("V" == TYPE) && (3 <= l)) | ||
| 725 | l = wordLength; // BREAK! | ||
| 726 | else if (("R" == TYPE) && (4 <= l)) | ||
| 727 | l = wordLength; // BREAK! | ||
| 728 | next = llList2String(words, w + 1 + l); | ||
| 729 | } | ||
| 730 | if (">" != llGetSubString(value, -1, -1)) // Seems OpenSim at least can cast partial vectors. | ||
| 731 | value = ""; | ||
| 732 | if ("V" == TYPE) | ||
| 733 | { | ||
| 734 | vector v = (vector) value; | ||
| 735 | value = (string) v; | ||
| 736 | } | ||
| 737 | else if ("R" == TYPE) | ||
| 738 | { | ||
| 739 | rotation r = (rotation) value; | ||
| 740 | value = (string) r; | ||
| 741 | } | ||
| 742 | } | ||
| 743 | } | ||
| 744 | else if ("L" == TYPE) | ||
| 745 | { | ||
| 746 | string first = llList2String(words, w); | ||
| 747 | string last = llList2String(words, w + 1); | ||
| 748 | |||
| 749 | if (osIsUUID(first)) | ||
| 750 | value = first; | ||
| 751 | else if ("" != last) | ||
| 752 | { | ||
| 753 | list avatars = [llGetOwner(), ZERO_VECTOR, llKey2Name(llGetOwner())] + osGetAvatarList(); // Strided list, UUID, position, name. | ||
| 754 | integer avaLength = llGetListLength(avatars); | ||
| 755 | string aName = llToLower(first + " " + last); | ||
| 756 | integer n; | ||
| 757 | |||
| 758 | for (n = 0; n < avaLength; n +=3) | ||
| 759 | { | ||
| 760 | if (llToLower(llList2String(avatars, n + 2)) == aName) | ||
| 761 | { | ||
| 762 | value = llKey2Name(llList2String(avatars, n)); | ||
| 763 | ++w; | ||
| 764 | n = avaLength; // BREAK! | ||
| 765 | } | ||
| 766 | } | ||
| 767 | } | ||
| 768 | if (("" == value) && ("" != first)) // Try scanning the sim for a matching first or last name. | ||
| 769 | { | ||
| 770 | list candidates = []; | ||
| 771 | list avatars = [llGetOwner(), ZERO_VECTOR, llKey2Name(llGetOwner())] + osGetAvatarList(); // Strided list, UUID, position, name. | ||
| 772 | integer avaLength = llGetListLength(avatars); | ||
| 773 | integer n = llSubStringIndex(first, "'"); | ||
| 774 | |||
| 775 | // Check if we are searching for multiples. | ||
| 776 | // There can be only one multiple, and it should be the first, | ||
| 777 | // so skip multiples checking if multiple is set already. | ||
| 778 | if ((-1 != n) && (-1 == multiple)) | ||
| 779 | { | ||
| 780 | multiple = a; | ||
| 781 | first = llGetSubString(first, 0, n - 1); | ||
| 782 | } | ||
| 783 | last = llToLower(first); | ||
| 784 | for (n = 0; n < avaLength; n +=3) | ||
| 785 | { | ||
| 786 | list names = llParseString2List(llToLower(llList2String(avatars, n + 2)), [" "], []); | ||
| 787 | |||
| 788 | if ((llList2String(names, 0) == last) || (llList2String(names, 1) == last)) | ||
| 789 | candidates += [llList2String(avatars, n)]; | ||
| 790 | } | ||
| 791 | avaLength = llGetListLength(candidates); | ||
| 792 | if (0 == avaLength) | ||
| 793 | llSay(0, "No one matching the name " + first + " here."); | ||
| 794 | else if ((1 < avaLength) && (-1 == multiple)) | ||
| 795 | llSay(0, "More than one matching the name " + first + " here."); | ||
| 796 | else if (-1 != multiple) | ||
| 797 | value = llDumpList2String(candidates, "|"); | ||
| 798 | else | ||
| 799 | value = llKey2Name(llList2String(candidates, 0)); | ||
| 800 | } | ||
| 801 | } | ||
| 802 | else if ("N" == TYPE) | ||
| 803 | { | ||
| 804 | string first = llList2String(words, w); | ||
| 805 | string last = llList2String(words, w + 1); | ||
| 806 | |||
| 807 | if (osIsUUID(first)) | ||
| 808 | value = first; | ||
| 809 | else if ("" != last) | ||
| 810 | { | ||
| 811 | value = first + " " + last; | ||
| 812 | ++w; | ||
| 813 | } | ||
| 814 | } | ||
| 815 | else // The rest are "rest of string". | ||
| 816 | { | ||
| 817 | if (w < wordLength) | ||
| 818 | result += [llDumpList2String(llList2List(words, w, -1), " ")]; | ||
| 819 | w = wordLength; | ||
| 820 | } | ||
| 821 | result += [value]; | ||
| 822 | ++w; | ||
| 823 | if (w > wordLength) | ||
| 824 | a = argsCount; // BREAK! | ||
| 825 | } | ||
| 826 | |||
| 827 | // Put the rest of the words back together as "rest of message". | ||
| 828 | if (w < wordLength) | ||
| 829 | result += [llDumpList2String(llList2List(words, w, -1), " ")]; | ||
| 830 | //llSay(0, "ARGUMENTS for " + llList2String(words, 0) + " = " + llDumpList2String(arguments, "|")); | ||
| 831 | //llSay(0, "RESULTS " + llDumpList2String(result, "~")); | ||
| 832 | } | ||
| 833 | } | ||
| 834 | |||
| 835 | if ((1 + required) > wordLength) | ||
| 836 | { | ||
| 837 | // bitch | ||
| 838 | if (id != realId) | ||
| 839 | llInstantMessage(realId, "Not enough required arguments in a command from your object " + llKey2Name(id) + " The command was - " + message); | ||
| 840 | else | ||
| 841 | llInstantMessage(realId, "Not enough required arguments in your command. The command was - " + message); | ||
| 842 | } | ||
| 843 | else | ||
| 844 | { | ||
| 845 | // RETURNS incoming channel | incoming name | incoming key | incoming message | prefix | command | list of arguments | rest of message | ||
| 846 | if (-1 != multiple) | ||
| 847 | { | ||
| 848 | list candidates = llParseString2List(llList2String(result, 6 + multiple), ["|"], []); | ||
| 849 | integer candiLength = llGetListLength(candidates); | ||
| 850 | integer c; | ||
| 851 | |||
| 852 | for (c = 0; c < candiLength; ++c) | ||
| 853 | { | ||
| 854 | result = llListReplaceList(result, [llList2String(candidates, c)], 6 + multiple, 6 + multiple); | ||
| 855 | llMessageLinked(LINK_SET, UTILITIES_CHAT_DONE, llDumpList2String(result, LIST_SEP), (key) script); | ||
| 856 | } | ||
| 857 | } | ||
| 858 | else | ||
| 859 | llMessageLinked(LINK_SET, UTILITIES_CHAT_DONE, llDumpList2String(result, LIST_SEP), (key) script); | ||
| 860 | } | ||
| 861 | } | ||
| 862 | } | ||
| 863 | } | ||
| 864 | } | ||
| 865 | } | ||
| 866 | } | ||
| 867 | |||
| 868 | startNextRead() | ||
| 869 | { | ||
| 870 | if (0 < llGetListLength(settingsCards)) | ||
| 871 | { | ||
| 872 | settingsName = llList2String(settingsCards, 0); | ||
| 873 | settingsKey = llList2Key(settingsCards, 1); | ||
| 874 | settingsLine = 0; | ||
| 875 | settingsQueryID = llGetNotecardLine(settingsName, settingsLine); // request first line | ||
| 876 | settingsCards = llDeleteSubList(settingsCards, 0, 1); | ||
| 877 | } | ||
| 878 | else | ||
| 879 | { | ||
| 880 | settingsName = ".settings"; | ||
| 881 | settingsKey = NULL_KEY; | ||
| 882 | settingsQueryID = NULL_KEY; | ||
| 883 | } | ||
| 884 | } | ||
| 885 | |||
| 886 | list readThisLine(string data) | ||
| 887 | { | ||
| 888 | list result = []; | ||
| 889 | |||
| 890 | data = llStringTrim(data, STRING_TRIM); | ||
| 891 | if ((0 < llStringLength(data)) && ("#" != llGetSubString(data, 0, 0))) | ||
| 892 | { | ||
| 893 | list commands = llParseStringKeepNulls(data, [";"], []); | ||
| 894 | list new = []; | ||
| 895 | string newCommand = ""; | ||
| 896 | integer length = llGetListLength(commands); | ||
| 897 | integer i; | ||
| 898 | |||
| 899 | for (i = 0; i < length; ++i) | ||
| 900 | { | ||
| 901 | string command = llList2String(commands, i); | ||
| 902 | |||
| 903 | // Check for line continuation. I think. lol | ||
| 904 | if ("\\" == llGetSubString(command, -1, -1)) | ||
| 905 | newCommand += llGetSubString(command, 0, -2) + ";"; | ||
| 906 | else | ||
| 907 | { | ||
| 908 | command = llStringTrim(newCommand + command, STRING_TRIM); | ||
| 909 | if (0 < llStringLength(command)) | ||
| 910 | new += [command]; | ||
| 911 | newCommand = ""; | ||
| 912 | } | ||
| 913 | //llOwnerSay("|" + newCommand + "|" + command + "|"); | ||
| 914 | } | ||
| 915 | |||
| 916 | length = llGetListLength(new); | ||
| 917 | for (i = 0; i < length; ++i) | ||
| 918 | { | ||
| 919 | string name; | ||
| 920 | string value = llList2String(new, i); | ||
| 921 | integer equals = llSubStringIndex(value, "="); | ||
| 922 | |||
| 923 | name = ""; | ||
| 924 | if (0 <= equals) | ||
| 925 | { | ||
| 926 | name = llStringTrim(llGetSubString(value, 0, equals - 1), STRING_TRIM_TAIL); | ||
| 927 | if ((equals + 1) < llStringLength(value)) | ||
| 928 | value = llStringTrim(llGetSubString(value, equals + 1, -1), STRING_TRIM_HEAD); | ||
| 929 | else | ||
| 930 | value = ""; | ||
| 931 | } | ||
| 932 | else | ||
| 933 | { | ||
| 934 | name = value; | ||
| 935 | value = ""; | ||
| 936 | } | ||
| 937 | result += [name, value]; | ||
| 938 | } | ||
| 939 | } | ||
| 940 | ++settingsLine; | ||
| 941 | return result; | ||
| 942 | } | ||
| 943 | |||
| 944 | init() | ||
| 945 | { | ||
| 946 | llMessageLinked(LINK_SET, UTILITIES_RESET_DONE, "", llGetInventoryKey(llGetScriptName())); | ||
| 947 | // Pointless in OpenSim, always reports 16384. Pffft | ||
| 948 | //llOwnerSay("Free memory " + (string) llGetFreeMemory() + " in " + llGetScriptName()); | ||
| 949 | llSetTimerEvent(MENU_TIMEOUT); | ||
| 950 | } | ||
| 951 | |||
| 952 | |||
| 953 | default | ||
| 954 | { | ||
| 955 | state_entry() | ||
| 956 | { | ||
| 957 | init(); | ||
| 958 | } | ||
| 959 | |||
| 960 | on_rez(integer param) | ||
| 961 | { | ||
| 962 | init(); | ||
| 963 | } | ||
| 964 | |||
| 965 | attach(key attached) | ||
| 966 | { | ||
| 967 | init(); | ||
| 968 | } | ||
| 969 | |||
| 970 | listen(integer channel, string name, key id, string message) | ||
| 971 | { | ||
| 972 | myListen(channel, name, id, message); | ||
| 973 | } | ||
| 974 | |||
| 975 | // Handle commands from other scripts. | ||
| 976 | // Negative odd values of num are the commands, return num - 1 as the result. | ||
| 977 | link_message(integer sender_num, integer num, string value, key id) | ||
| 978 | { | ||
| 979 | list input = llParseStringKeepNulls(value, [LIST_SEP], []); | ||
| 980 | |||
| 981 | if (UTILITIES_RESET == num) // Request stuff to be reset | ||
| 982 | { | ||
| 983 | if ("reset" == value) // Request us to be reset. | ||
| 984 | llResetScript(); | ||
| 985 | resetPrimShit(); | ||
| 986 | llMessageLinked(LINK_SET, num - 1, value, id); | ||
| 987 | } | ||
| 988 | else if (UTILITIES_READ == num) // Request a notecard to be read. | ||
| 989 | { | ||
| 990 | list result = []; | ||
| 991 | integer length = osGetNumberOfNotecardLines(value); | ||
| 992 | |||
| 993 | settingsLine = 0; | ||
| 994 | while (settingsLine <= length) | ||
| 995 | { | ||
| 996 | result += readThisLine(osGetNotecardLine(value, settingsLine)); | ||
| 997 | if (0 == (settingsLine % 500)) // Don't send too many at once. | ||
| 998 | { | ||
| 999 | integer percent = (integer) ((((float) settingsLine) / ((float) length)) * 100.0); | ||
| 1000 | |||
| 1001 | llSay(0, "Reading '" + value + "' " + (string) percent + "% done."); | ||
| 1002 | // Sending a negative line to try to avoid triggering foreign scripts. | ||
| 1003 | // Sending from -1000 downwards to avoid triggering our scripts. | ||
| 1004 | llMessageLinked(LINK_SET, -1000 - settingsLine, llDumpList2String([value] + result, LIST_SEP), id); | ||
| 1005 | result = []; | ||
| 1006 | } | ||
| 1007 | } | ||
| 1008 | // Send the last batch. | ||
| 1009 | if (0 != llGetListLength(result)) | ||
| 1010 | llMessageLinked(LINK_SET, -1000 - settingsLine, llDumpList2String([value] + result, LIST_SEP), id); | ||
| 1011 | llMessageLinked(LINK_SET, UTILITIES_READ_DONE, value, id); | ||
| 1012 | } | ||
| 1013 | else if (UTILITIES_SUBSTITUTE == num) // Request a param substitution. | ||
| 1014 | { | ||
| 1015 | llMessageLinked(LINK_SET, num - 1, substitute([substitute(input, "%"), "n\n", "t\t", "\\\\", "\"\""], "\\"), id); | ||
| 1016 | } | ||
| 1017 | else if (UTILITIES_NEXT_WORD == num) // Get the next word | ||
| 1018 | { | ||
| 1019 | llMessageLinked(LINK_SET, num - 1, llDumpList2String(nextWord(llList2String(input, 0), llList2String(input, 1), llList2String(input, 2)), LIST_SEP), id); | ||
| 1020 | } | ||
| 1021 | else if (UTILITIES_MENU == num) // Request big menu to be displayed | ||
| 1022 | { | ||
| 1023 | startMenu(id, input); | ||
| 1024 | } | ||
| 1025 | else if (UTILITIES_CHAT == num) | ||
| 1026 | { | ||
| 1027 | // channel list | owner list | prefix list | command list | ||
| 1028 | list channels = llParseString2List(llList2String(input, 0), ["|"], []); | ||
| 1029 | list owners = llParseString2List(llList2String(input, 1), ["|"], []); | ||
| 1030 | list prefixes = llParseString2List(llList2String(input, 2), ["|"], []); | ||
| 1031 | string commands = llList2String(input, 3); | ||
| 1032 | integer chLength = llGetListLength(channels); | ||
| 1033 | integer i; | ||
| 1034 | |||
| 1035 | for (i = 0; i < chLength; ++i) | ||
| 1036 | { | ||
| 1037 | string channel = llList2String(channels, i); | ||
| 1038 | integer j; | ||
| 1039 | |||
| 1040 | if ("" != commands) | ||
| 1041 | { | ||
| 1042 | //llSay(0, "ADDING " + channel + "= " + commands); | ||
| 1043 | integer oLength = llGetListLength(owners); | ||
| 1044 | integer pLength = llGetListLength(prefixes); | ||
| 1045 | integer found = llListFindList(channelHandles, [channel]); | ||
| 1046 | |||
| 1047 | if ("0" == channel) | ||
| 1048 | llOwnerSay("WARNING: Script using local chat for commands may cause lag - " + llKey2Name(id)); | ||
| 1049 | chatChannels = addChatScripts(chatChannels, channel, (string) id, 1); | ||
| 1050 | chatCommands = addChatScripts(chatCommands, (string) id + channel, commands, 2); | ||
| 1051 | for (j = 0; j < oLength; ++j) | ||
| 1052 | chatOwners = addChatScripts(chatOwners, llList2String(owners, j) + channel, (string) id, 1); | ||
| 1053 | |||
| 1054 | for (j = 0; j < pLength; ++j) | ||
| 1055 | { | ||
| 1056 | string prefix = llList2String(prefixes, j); | ||
| 1057 | |||
| 1058 | if (" " == llGetSubString(prefix, -1, -1)) | ||
| 1059 | chatPrefixes = addChatScripts(chatPrefixes, channel + " " + llGetSubString(prefix, 0, -2), (string) id, 1); | ||
| 1060 | else | ||
| 1061 | chatPrefixes2 = addChatScripts(chatPrefixes2, channel + " " + prefix, (string) id, 1); | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | // Do this last, so all the rest is setup already. | ||
| 1065 | if (0 > found) | ||
| 1066 | { | ||
| 1067 | //llOwnerSay("LISTEN " + channel + " " + llList2String(owners, j)); | ||
| 1068 | // TODO - Closing and reopening that channel if details change. | ||
| 1069 | // NOTE - only the FIRST owner is supported. | ||
| 1070 | // TODO - this is not right anyway. | ||
| 1071 | // If a channel gets more than one owner from different invocations of this command, | ||
| 1072 | // then it should re open the listener. | ||
| 1073 | if (1 == oLength) | ||
| 1074 | channelHandles += [channel, llListen((integer) channel, "", llList2Key(owners, 0), "")]; | ||
| 1075 | else | ||
| 1076 | channelHandles += [channel, llListen((integer) channel, "", NULL_KEY, "")]; | ||
| 1077 | } | ||
| 1078 | } | ||
| 1079 | else // if ("" != commands) | ||
| 1080 | { | ||
| 1081 | integer index = llListFindList(chatCommands, [(string) id + channel]); | ||
| 1082 | // Yes, I know, UUIDs are a fixed length, but there's talk of using SHA1 hashes instead. | ||
| 1083 | integer keyLength = llStringLength((string) id); | ||
| 1084 | integer length; | ||
| 1085 | |||
| 1086 | chatChannels = delChatScripts(chatChannels, channel, (string) id, 1); | ||
| 1087 | if (0 <= index) | ||
| 1088 | chatCommands = llDeleteSubList(chatCommands, index, index + 1); | ||
| 1089 | |||
| 1090 | length = llGetListLength(chatOwners); | ||
| 1091 | for (j = 0; j < length; j += 2) | ||
| 1092 | { | ||
| 1093 | string this = llList2String(chatOwners, j); | ||
| 1094 | |||
| 1095 | if (llGetSubString(this, keyLength, -1) == channel) | ||
| 1096 | chatOwners = delChatScripts(chatOwners, this, (string) id, 1); | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | // TODO - go through chatPrefixes/2 removing script from any on this channel | ||
| 1100 | |||
| 1101 | if (0 > llListFindList(chatChannels, [channel])) | ||
| 1102 | { | ||
| 1103 | length = llGetListLength(channelHandles); | ||
| 1104 | for (j = 0; j < length; j += 2) | ||
| 1105 | { | ||
| 1106 | if (llList2String(channelHandles, j) == channel) | ||
| 1107 | { | ||
| 1108 | llListenRemove(llList2Integer(channelHandles, j + 1)); | ||
| 1109 | channelHandles = llDeleteSubList(channelHandles, j, j + 1); | ||
| 1110 | length -= 2; | ||
| 1111 | j -= 2; | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | } | ||
| 1115 | //llOwnerSay("owners " + llDumpList2String(chatOwners, "^")); | ||
| 1116 | //llOwnerSay("channels " + llDumpList2String(chatChannels, "^")); | ||
| 1117 | //llOwnerSay("prefixes " + llDumpList2String(chatPrefixes, "^")); | ||
| 1118 | //llOwnerSay("prefixes2 " + llDumpList2String(chatPrefixes2, "^")); | ||
| 1119 | //llOwnerSay("commands " + llDumpList2String(chatCommands, "^")); | ||
| 1120 | } // if ("" != commands) | ||
| 1121 | } // for (i = 0; i < chLength; ++i) | ||
| 1122 | |||
| 1123 | } | ||
| 1124 | else if (UTILITIES_CHAT_FAKE == num) | ||
| 1125 | { | ||
| 1126 | myListen(llList2Integer(input, 0), llList2String(input, 1), llList2Key(input, 2), llList2String(input, 3)); | ||
| 1127 | } | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | touch_start(integer num) | ||
| 1131 | { | ||
| 1132 | integer length = llGetListLength(registeredMenus); | ||
| 1133 | integer i; | ||
| 1134 | |||
| 1135 | // Scan through the list, checking if those scripts still exist in inventory. | ||
| 1136 | for (i = length - 1; i >= 0; --i) | ||
| 1137 | { | ||
| 1138 | string this = llList2String(llParseStringKeepNulls(llList2String(registeredMenus, 0), ["|"], []), 1); | ||
| 1139 | |||
| 1140 | if (INVENTORY_NONE == llGetInventoryType(this)) | ||
| 1141 | { | ||
| 1142 | registeredMenus = llDeleteSubList(registeredMenus, i, i); | ||
| 1143 | --length; | ||
| 1144 | } | ||
| 1145 | } | ||
| 1146 | for (i = 0; i < num; ++i) | ||
| 1147 | { | ||
| 1148 | key id = llDetectedKey(i); | ||
| 1149 | string desc = llList2String(llGetObjectDetails(llGetLinkKey(llDetectedLinkNumber(i)), [OBJECT_DESC]), 0); | ||
| 1150 | |||
| 1151 | // If there's a description, then it's likely a scriptlet. | ||
| 1152 | if ("" != desc) | ||
| 1153 | myListen(0, llKey2Name(id), id, desc); // TODO - the problem here is that the first argument is a channel, | ||
| 1154 | // and we don't know which channel to fake. | ||
| 1155 | // Maybe use the debug channel as a wildcard? | ||
| 1156 | else if (1 == length) // Only one registered, select it directly. | ||
| 1157 | { | ||
| 1158 | llMessageLinked(LINK_SET, UTILITIES_MENU_DONE, llDumpList2String([id, ""], LIST_SEP), | ||
| 1159 | llList2String(llParseStringKeepNulls(llList2String(registeredMenus, 0), ["|"], []), 2)); | ||
| 1160 | } | ||
| 1161 | else if (0 != length) // More than one, put up a menu of them. | ||
| 1162 | startMenu(NULL_KEY, [id, INVENTORY_NONE, "Choose a function :"] + registeredMenus); | ||
| 1163 | // If there's zero registered menus, then do nothing. | ||
| 1164 | } | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | timer() | ||
| 1168 | { | ||
| 1169 | integer length = llGetListLength(menus); | ||
| 1170 | float time = llGetTime(); | ||
| 1171 | integer i; | ||
| 1172 | |||
| 1173 | // Run through menus, removing any that timed out. | ||
| 1174 | for (i = 0; i < length; i += MENU_STRIDE) | ||
| 1175 | { | ||
| 1176 | if (time > (llList2Float(menus, i + MENU_TIME) + MENU_TIMEOUT)) | ||
| 1177 | { | ||
| 1178 | integer menuHandle = llList2Integer(menus, i + MENU_HANDLE); | ||
| 1179 | |||
| 1180 | llSay(0, "Menu for " + llKey2Name(llList2String(menus, i + MENU_USER)) + " timed out."); | ||
| 1181 | if (menuHandle) | ||
| 1182 | llListenRemove(menuHandle); | ||
| 1183 | menus = llDeleteSubList(menus, i, i + MENU_STRIDE - 1); | ||
| 1184 | length = llGetListLength(menus); | ||
| 1185 | i -= MENU_STRIDE; | ||
| 1186 | } | ||
| 1187 | } | ||
| 1188 | } | ||
| 1189 | } | ||
