2 // huffware script: jaunt rezolator, by fred huffhines.
4 // this script supports the jaunt wik rez script by dealing with the rezzed objects.
6 // this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
7 // do not use it in objects without fully realizing you are implicitly accepting that license.
10 // global constants that can be changed to good effect.
12 integer DEBUGGING = FALSE; // set this to true for noisier run times.
14 // important constants used internally... these should not be changed willy nilly.
16 // begin jaunt base API:
18 // the following constants define how the script should behave (i.e., its conveyance mode).
19 // TWO_WAY_TRIP: the script jaunts using the current target vectors to get somewhere
20 // and then takes the same pathway back, but in reverse order.
21 // AUTOREZ_JAUNTER: the script rezzes the first regular object in its inventory next to
22 // the root telehub. that object is loaded with the destination notecard and this script.
23 // the rezzed object can then be used for the next few minutes to jaunt to the selected
24 // destination. the temporary object will use the ONE_WAY_TRIP mode.
25 // ONE_WAY_TRIP: the object containing this script will take the user to a particular
26 // destination, but the object does not survive the trip. it self-destructs after
27 // reaching the destination. this mode is used in conjunction with the AUTOREZ_JAUNTER
28 // mode and should generally never be used on its own.
29 // RECONNAISSANCE_TRIP: a survey run to test out a particular path to get to a
31 integer TWO_WAY_TRIP = 1;
32 integer AUTOREZ_JAUNTER = 2;
33 integer ONE_WAY_TRIP = 3;
34 integer RECONNAISSANCE_TRIP = 4;
36 // indicates a reconnaissance command when found as a prefix on chat text
37 // on our official recon channel.
38 string CHILD_CHAT_TEXT = "#rcn";
39 string READY_TEXT = "-y"; // lets us know that a jaunter is ready to be filled.
40 string RETURN_WORD = "-b"; // used to signal a returning recong jaunter.
42 // values used to represent different stages of verification.
43 integer VERIFY_UNTESTED = -3; // don't know destinations tate yet.
44 integer VERIFY_SAFE = -4; // the destination last tested safe.
45 integer VERIFY_UNSAFE_SO_FAR = -5; // this cannot be done with simple jaunt.
46 integer VERIFY_UNSAFE_DECIDED = -6; // this means the destination seems intractable.
48 integer ONE_WAY_TRICKY_PARM = -100000;
49 // used to signal to the one way jaunter that it's taking a single pathway.
50 integer RECONNAISSANCE_TRICKY_PARM = -200000;
51 // on_rez parm indicates that this will be a recon trip to check out a path.
52 integer MAXIMUM_PRIV_CHAN = 90000;
53 // the largest amount we will ever subtract from the tricky parms in order to
54 // tell the sub-jaunter which channel to listen on.
56 string VECTOR_SEPARATOR = "|";
57 // how we separate components of vectors from each other.
58 string DB_SEPARATOR = "``"; // separating items in lists.
60 // end of jaunt base API.
62 integer REZ_KID_FIELDS_NEEDED = 6; // the rez kid API method needs this many parms.
64 integer MAXIMUM_RECON_ATTEMPTS = 7;
65 // maximum tries to find a path for the hard cases (after first simple jaunt fails).
67 float TIMER_PERIOD = 1.0; // we run a pretty slow timer to check on destinations.
69 integer MAXIMUM_SNOOZES = 20; // how long we let a pending action go before declaring it failed.
71 string OUR_COW_TAG = "l_jrzltr_dc";
72 // a hopefully unique id for this script to talk to the data cow with.
74 // the actions that can be held in the action queue.
75 integer AQ_ONEWAY_JAUNTER_VOYAGE = 40; // information will be used for one-way jaunter.
76 integer AQ_PREP_RECON_JAUNTER = 41; // info for the reconnaissance jaunter to go check out.
77 integer AQ_CHECK_HARD_CASE = 42; // working on hard cases to get to destination somehow.
78 integer AQ_ACQUIRE_BASE_PATH = 43; // we are grabbing a random path that we think is healthy.
80 string QUADRANT_TAG_NAME = "q:"; // a piece of text we put in front of generated destinations.
82 integer MAXIMUM_SAFE_LOCATIONS = 28; // we'll track this many extra locations.
83 float MINIMUM_DISTANCE_FOR_EXTRAS = 10.0; // how far a new locale must be to add.
85 integer MAX_CHAT_REPEAT = 3; // give instructions N times in case things are slow.
86 float SLEEPY_TIME = 0.02; // how long to snooze between chats.
92 list global_verifications; // we gradually acquire knowledge of the health of the destinations.
93 string global_object_name; // the object to rez from inventory.
94 string global_destination_name; // the name of the place where the jaunter should go.
95 vector global_rez_place; // starting location for the rezzed jaunter.
96 integer conveyance_mode; // how the rezzed jaunter should process its destination.
98 integer private_id_from_listen; // set when we listen to our private channel.
99 integer private_chat_channel; // where sub-jaunters communicate with root.
101 integer serving_root; // if this is true, it means we are serving a root jaunter.
103 integer destinations_total; // how many destinations are known in total? we compute this.
104 integer destinations_real; // how many are user-configured destinations?
106 integer snooze_counter; // how many times have we slept during a process?
108 integer heard_from_root; // true if the root jaunter has told us (a child jaunter) where to go.
110 // recon variables...
111 integer is_recon_pending; // are we awaiting a reconnaissance?
112 integer trying_hard_cases; // are we already working on the tough ones?
113 integer current_recon_index; // where is the recon jaunter testing.
114 integer recon_reattempts; // how many tries did we take?
115 integer done_reconnoiter; // is recon process finished?
116 integer succeeded_recon; // set to true if close enough.
117 integer good_destinations_counted; // a tally of how many destinations looked healthy.
118 integer ungenerated_good_destinations_counted; // counts only user selected good dests.
119 vector final_destination; // where the recon process is really headed.
120 integer partial_path_count; // the counter for partial path replies.
121 list safe_locales_seen; // locations that we have a safe route to, as reported by recons.
123 // jaunt trip variables...
124 list full_journey; // the full pathway we expect to jaunt on.
125 integer last_verification; // the last state we heard.
127 // root jaunter variables...
128 integer child_needs_setup;
129 // true when a child has been rezzed and still needs to be filled with the script.
131 // the API for this library script to be used in other scripts.
133 // do not redefine these constants.
134 integer JAUNT_REZOLATOR_HUFFWARE_ID = 10025;
135 // the unique id within the huffware system for this script's commands.
136 // it's used in llMessageLinked as the num parameter.
137 string HUFFWARE_PARM_SEPARATOR = "{~~~}";
138 // this pattern is an uncommon thing to see in text, so we use it to separate
139 // our commands in link messages.
140 string HUFFWARE_ITEM_SEPARATOR = "{|||}";
141 // used to separate lists of items from each other when stored inside a parameter.
142 // this allows lists to be passed as single string parameters if needed.
143 integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs.
145 // commands available from the library:
146 string RESET_REZOLATOR = "#reset";
147 // tells the script to stop any previous efforts to rez children.
148 string REZ_CHILD_NOW = "#rezkd#";
149 // requests this script to create a new child object. this requires several
150 // parameters to succeed: 1) object to rez, 2) conveyance mode for the rezzed object
151 // to implement, 3) the chat channel to listen for and speak to new child on,
152 // 4) the destination name for where the child should go, 5) a count of the full
153 // set of known destinations, 6) the target where the jaunt should arrive at.
154 string REPORT_CHILD_REZZED = "#reziam";
155 // requests that this class report to the root jaunter that a child has rezzed and
156 // is ready for service. there are no required parameters.
157 //hmmm: combine the above with the below!
158 string REZOLATOR_CHILD_SUPPORT = "#rzsup";
159 // used by child jaunters to request that the rezolator handle things for them.
160 // the first parameter is the startup parameter handed to the control script.
161 string REZOLATOR_CHILD_RETURNED = "#rezdon";
162 // used by the child jaunter to tell the rezolator that it has jumped to wherever it
163 // was supposed to and is ready to report to the parent, if needed. the first parameter
164 // required for the rezolator is whether the jump was successful (1) or not (0), and
165 // the second parameter should be the last safe position that the jaunter was at when
166 // it was possibly closest to the target.
168 // events generated by the library:
169 string REZOLATOR_EVENT_REZZED_CHILD = "#donekd";
170 // an event generated for a previous rez child request. this lets the caller know that
171 // the child has been created and told what to do. the single parameter is where the
172 // jaunter has been rezzed.
173 string REZOLATOR_EVENT_GOT_INSTRUCTIONS = "#rzsta";
174 // the root jaunter has given a child instructions on where to go. this info includes
175 // the name of the destination, the pathway to get there, and the conveyance mode.
176 string REZOLATOR_EVENT_RECON_FINISHED = "#rzcnfn";
177 // an event generated when the recon process has concluded. this includes parms:
178 // number of good destinations.
182 // requires click action API...
184 integer CHANGE_CLICK_ACTION_HUFFWARE_ID = 10024;
185 // a unique ID within the huffware system for this script.
186 // the API only supports the service number; there are no commands to pass in the "msg"
187 // parameter. the id does accept a parameter, which is the type of click action to begin
188 // using for this prim.
191 // requires data cow library...
193 // do not redefine these constants.
194 integer DATA_COW_HUFFWARE_ID = 10017;
195 // the unique id within the huffware system for the jaunt script to
196 // accept commands on. this is used in llMessageLinked as the num parameter.
198 //string RESET_LIST_COMMAND = "reset_L"; // flushes out the currently held list.
199 string ADD_ITEM_COMMAND = "add_I";
200 // adds items to the list. this is a list of pairs of name/value, where the name is
201 // how the item will be looked up from the list, and the value is the contents to be stored.
202 //string REMOVE_ITEM_COMMAND = "rm_I";
203 // accepts a list of names for items. all the mentioned ones will be removed from the list.
204 //string GET_ITEM_COMMAND = "get_I";
205 // retrieves the item's contents for a given name. first parm is the name. if there
206 // are other parameters, then they are taken as other items to return also.
207 // the return data will be pairs of <name, entry> for each of the names in the request.
208 string TAGGED_GET_ITEM_COMMAND = "get_T";
209 // like get item, except the first parameter is an identifier that the caller wants to
210 // use to tag this request. the other parameters are still taken as names. the response
211 // will contain the tag as the first item, then the <name, entry> pairs that were found.
214 // sets up the initial state, if this script has just started, or it
215 // cleans up the state, if the script was already running.
216 initialize_rezolator()
218 if (DEBUGGING) log_it("start mem=" + (string)llGetFreeMemory());
219 global_rez_place = llGetPos();
220 heard_from_root = FALSE; // we don't know root from adam yet.
223 // sets up the rezolator to be a child jaunter and report to a root jaunter.
224 provide_child_support(integer startup_parm)
226 serving_root = FALSE;
227 // we are serving as a special purpose jaunter object.
228 // set our method of jaunting based on the secret signal.
229 if (-(startup_parm - ONE_WAY_TRICKY_PARM) < MAXIMUM_PRIV_CHAN) {
230 conveyance_mode = ONE_WAY_TRIP;
231 private_chat_channel = startup_parm - ONE_WAY_TRICKY_PARM;
232 if (DEBUGGING) log_it("one way child: private chat on " + private_chat_channel);
233 } else if (-(startup_parm - RECONNAISSANCE_TRICKY_PARM) < MAXIMUM_PRIV_CHAN) {
234 conveyance_mode = RECONNAISSANCE_TRIP;
235 private_chat_channel = startup_parm - RECONNAISSANCE_TRICKY_PARM;
236 if (DEBUGGING) log_it("recon child: private chat on " + private_chat_channel);
239 if (private_id_from_listen) {
240 // toss old listening since we're working on a new deal now.
241 llListenRemove(private_id_from_listen);
243 // listen to chats on our special channel.
244 private_id_from_listen = llListen(private_chat_channel, "", NULL_KEY, "");
245 if (DEBUGGING) log_it("listening for cmds on chan " + (string)private_chat_channel);
248 // this handles the case where we need to send out another seeker drone.
249 // true is returned if this function thinks things are doing well.
250 integer advance_reconnaissance_position()
252 if (!trying_hard_cases) {
253 list added_parms = [];
254 if (current_recon_index < 0) {
255 // this is a signalled condition. we have just started chowing through
256 // the destinations to look at them.
257 current_recon_index = 0;
259 // a normal move to the next slot now.
260 current_recon_index++;
263 log_it("total dests are " + destinations_total
264 + " and we're at indy " + current_recon_index);
265 if (current_recon_index <= destinations_total - 1) {
266 // try jumping to the next place.
267 if (DEBUGGING) log_it("next normal test, index " + (string)current_recon_index);
268 push_action_record(AQ_PREP_RECON_JAUNTER, added_parms);
270 // we've gone off the edge of our list in normal mode.
271 // now check those that are marked as failures.
272 trying_hard_cases = TRUE;
273 if (DEBUGGING) log_it("started trying hard cases, back to index 0");
274 // start over in processing the list.
275 current_recon_index = 0;
276 is_recon_pending = FALSE;
277 good_destinations_counted = 0;
278 ungenerated_good_destinations_counted = 0;
279 push_action_record(AQ_CHECK_HARD_CASE, added_parms);
281 llMessageLinked(LINK_THIS, DATA_COW_HUFFWARE_ID, TAGGED_GET_ITEM_COMMAND,
282 wrap_parameters([OUR_COW_TAG, "#" + (string)current_recon_index]));
285 if ((current_recon_index < destinations_total) && trying_hard_cases) {
286 // if the conditions are very right or very wrong, move to next place.
287 if ( (last_verification == VERIFY_SAFE) || (last_verification == VERIFY_UNSAFE_DECIDED)
288 || (current_recon_index < 0) ) {
289 current_recon_index++;
291 if (DEBUGGING) log_it("next hard test at index " + (string)current_recon_index);
292 // make sure we're still in range.
293 if (current_recon_index < destinations_total) {
294 // we're running the hard cases now, so go to the next one of those.
295 list added_parms = [];
296 push_action_record(AQ_CHECK_HARD_CASE, added_parms);
297 llMessageLinked(LINK_THIS, DATA_COW_HUFFWARE_ID, TAGGED_GET_ITEM_COMMAND,
298 wrap_parameters([OUR_COW_TAG, "#" + (string)current_recon_index]));
303 if (!done_reconnoiter) {
304 if (DEBUGGING) log_it("decided we're done with recon.");
305 // all have been checked.
306 done_reconnoiter = TRUE;
307 // let the root know how many were tasty healthy places.
308 llMessageLinked(LINK_THIS, JAUNT_REZOLATOR_HUFFWARE_ID + REPLY_DISTANCE,
309 REZOLATOR_EVENT_RECON_FINISHED,
310 (string)ungenerated_good_destinations_counted);
312 if (DEBUGGING) log_it("doing nothing else in advance.");
313 return FALSE; // not good.
316 // considers the current state of affairs and schedules the next reasonable thing
317 // that needs to be done to move the rezzing process along.
318 take_the_next_appropriate_action()
320 // see if the child setup process is still running.
321 if (child_needs_setup || is_recon_pending || (llGetListLength(action_queue) > 0) ) {
322 // we're still waiting on something. make sure it hasn't timed out.
323 if (!check_for_child_timeout()) return; // we still have time.
324 // saying this one failed, so we set it such that we are not waiting for kids
325 // or recons or the data cow right now.
327 if (child_needs_setup) {
328 if (DEBUGGING) log_it("TNAP: child needs setup...");
329 child_needs_setup = FALSE;
330 if (conveyance_mode == ONE_WAY_TRIP) {
331 //log_it("TNAP: telling root one way kid is hosed.");
332 // the startup of a child jaunter has failed. let the root think we're
333 // doing okay so it can get on with its life.
334 llMessageLinked(LINK_THIS, JAUNT_REZOLATOR_HUFFWARE_ID + REPLY_DISTANCE,
335 REZOLATOR_EVENT_REZZED_CHILD, (string)global_rez_place);
338 //log_it("TNAP: recon fail for kid needs setup, fall through.");
340 } else if (is_recon_pending) {
341 // the current reconnaissance drone has crashed apparently. deal with it.
342 if (DEBUGGING) log_it("TNAP: setting recon pend to false");
343 is_recon_pending = FALSE;
345 } else if (llGetListLength(action_queue) > 0) {
346 // action_queue must be non-empty, or we wouldn't be here.
347 list action_rec = pop_action_record(); // what else can we do?
348 if (DEBUGGING) log_it("TNAP: time-out on action: " + (string)action_rec);
351 // proceed to the next steps which are probably remedial.
354 if (DEBUGGING) log_it("TNAP: nothing pending, move things along...");
356 // nothing is pending right now.
357 if (conveyance_mode == ONE_WAY_TRIP) {
358 // this means we are done, since the only thing we do is start the kid up.
359 llSetTimerEvent(0.0);
363 // if the recon advancement returns true, then we are all set for the next place.
364 if (advance_reconnaissance_position()) return;
366 // if we got to here, then we need to do something about being past end of list...
368 if (done_reconnoiter) {
369 // all done with that, so we don't need the timer anymore.
370 llSetTimerEvent(0.0); // stop coming here.
371 return; // don't need to do any more.
374 if (trying_hard_cases) {
375 try_the_hard_cases();
378 if (DEBUGGING) log_it("fell completely through TNAP()");
381 // tries to crack the hard cases using added random jaunt destinations.
384 if (DEBUGGING) log_it("hardcase performing recon re-attempt, name=" + global_destination_name);
385 if (is_prefix(global_destination_name, QUADRANT_TAG_NAME)) {
386 //log_it("seeing that " + global_destination_name + " is a generated dest, ignoring.");
387 splice_destination_info(current_recon_index, "#" + (string)current_recon_index,
388 vector_chop(final_destination), VERIFY_UNSAFE_DECIDED);
389 advance_reconnaissance_position();
392 // count that as a failure, since we're not still on our first run through the list.
394 if (recon_reattempts > MAXIMUM_RECON_ATTEMPTS) {
395 // we have totally failed to find a workable pathway. we'll store the
396 // most simple form of the path back as the one to use.
397 log_it("total failure reaching " + (string)final_destination);
398 splice_destination_info(current_recon_index, "#" + (string)current_recon_index,
399 vector_chop(final_destination), VERIFY_UNSAFE_DECIDED);
400 recon_reattempts = 0;
401 advance_reconnaissance_position();
405 // start the process of finding a good random path.
406 if (!load_random_safe_path()) {
407 // can't get there from anywhere else, so try from here.
408 log_it("could not find a good random path!");
409 scramble_to_find_a_way([]);
413 // called to start a jaunter in the appropriate mode.
414 rez_requested_child(string object_name, integer conv_mode,
415 integer chat_channel, string destination_name, integer all_dests_count,
416 string target_location)
419 log_it("rez req: " + object_name + " convey=" + (string)conveyance_mode + " chat=" + (string)chat_channel + " dest=" + destination_name + " fullcount=" + (string)all_dests_count + " targ=" + target_location);
421 if (private_id_from_listen) {
422 // toss old listening since we're working on a new deal now.
423 llListenRemove(private_id_from_listen);
425 // listen to chats on our special channel.
426 private_id_from_listen = llListen(chat_channel, "", NULL_KEY, "");
428 // remember the info so we can tell our new child.
429 conveyance_mode = conv_mode;
430 global_destination_name = destination_name;
431 global_object_name = object_name; // the object to rez from inventory.
432 private_chat_channel = chat_channel;
433 full_journey = llParseString2List(target_location, [VECTOR_SEPARATOR], []);
434 destinations_total = all_dests_count;
435 global_verifications = []; // clear any old verifications.
437 for (indy = 0; indy < destinations_total; indy++)
438 global_verifications += [ VERIFY_UNTESTED ];
440 if (conveyance_mode == RECONNAISSANCE_TRIP) {
441 // we need to start reconnaissance mode.
442 current_recon_index = -1; // a signal that we haven't started.
444 is_recon_pending = FALSE;
445 done_reconnoiter = FALSE;
447 //turned off currently. doesn't seem to help much.
449 // experimental: add the quadrants of the sim as destinations. if some of these
450 // are reachable, then they should help us out.
451 destinations_real = destinations_total;
454 for (x = 32; x <= 255; x += 64) {
455 for (y = 32; y <= 255; y += 64) {
456 vector calculated = <x, y, 0.0>;
457 float height = llGround(calculated - llGetPos());
458 calculated.z = height + 64;
459 add_destination(QUADRANT_TAG_NAME + (string)x + "," + (string)y,
460 (string)adjust_to_ground(<x, y, 0>, 64), VERIFY_UNTESTED);
465 take_the_next_appropriate_action();
467 // create the jaunter object for a one way trip.
468 rez_a_jaunter(object_name, ONE_WAY_TRICKY_PARM + chat_channel, conveyance_mode);
470 llSetTimerEvent(TIMER_PERIOD);
473 // handles responses about our list coming back from the data cow.
474 answer_to_the_cow(string msg, string id, list parms)
476 string tag = llList2String(parms, 0);
477 if (tag != OUR_COW_TAG) return; // was not for us.
478 parms = llDeleteSubList(parms, 0, 0); // trim out the tag.
479 list action_record = pop_action_record();
480 integer actyo = extract_action_code(action_record);
481 // get the name out of the set first.
482 list split = llParseString2List(llList2String(parms, 1), [HUFFWARE_ITEM_SEPARATOR], []);
484 if (actyo != AQ_ACQUIRE_BASE_PATH) {
485 // it's very important that when we're acquiring other targets' path components,
486 // we do not mistake that for our own state for the path we're trying to reach.
487 // thus we only record the answer for actions that are about our real target.
488 global_destination_name = llList2String(parms, 0);
489 last_verification = llList2Integer(split, 1);
490 full_journey = prechewed_destinations(llList2String(split, 0));
492 log_it("just heard verif=" + (string)last_verification + " for " + global_destination_name);
495 if (actyo == AQ_ONEWAY_JAUNTER_VOYAGE) {
496 //log_it("one way, saying on chan " + (string)private_chat_channel + " -- " + global_destination_name + " fullj=" + (string)full_journey);
498 child_needs_setup = FALSE; // count off the one we just set up.
500 integer indy; // i told you N times.
501 for (indy = 0; indy < MAX_CHAT_REPEAT; indy++) {
502 llSay(private_chat_channel, CHILD_CHAT_TEXT
503 + global_destination_name
505 + llDumpList2String(full_journey, VECTOR_SEPARATOR));
506 // pausing to let our words sink in.
507 llSleep(SLEEPY_TIME);
510 // tell the root jaunter that this kid has been issued.
511 llMessageLinked(LINK_THIS, JAUNT_REZOLATOR_HUFFWARE_ID + REPLY_DISTANCE,
512 REZOLATOR_EVENT_REZZED_CHILD, (string)global_rez_place );
513 } else if (actyo == AQ_PREP_RECON_JAUNTER) {
514 // we got info about a place to try. record the last verif state locally.
515 if (DEBUGGING) log_it("info re destination: verif = " + last_verification);
516 splice_just_verification(current_recon_index, last_verification);
517 if ( (last_verification == VERIFY_UNTESTED)
518 || (trying_hard_cases && (last_verification == VERIFY_UNSAFE_SO_FAR) ) ) {
519 // this one is not tested yet or had problems. try it now.
520 //log_it("testing unchecked locale.");
521 is_recon_pending = TRUE;
522 // if the destination is claiming it's untested, we fix that now untrue statement.
523 if (last_verification == VERIFY_UNTESTED) {
524 splice_destination_info(current_recon_index, "#" + (string)current_recon_index,
525 llDumpList2String(full_journey, VECTOR_SEPARATOR), VERIFY_UNSAFE_SO_FAR);
528 log_it("rezzing jaunter for: name " + global_destination_name + " journ" + (string)full_journey);
529 rez_a_jaunter(global_object_name,
530 RECONNAISSANCE_TRICKY_PARM + private_chat_channel, RECONNAISSANCE_TRIP);
532 // this one doesn't need a recon.
533 is_recon_pending = FALSE;
534 //log_it("skipping recon for verif " + (string)last_verification);
535 advance_reconnaissance_position();
537 } else if (actyo == AQ_CHECK_HARD_CASE) {
538 // we got an answer to our request for a new hard locale to try.
539 // remember the verification state locally.
540 splice_just_verification(current_recon_index, last_verification);
542 if ( (last_verification == VERIFY_UNTESTED) || (last_verification == VERIFY_UNSAFE_SO_FAR) ) {
543 // this one is not tested yet or had problems. try it now.
545 log_it("testing hard case locale at " + (string)current_recon_index
546 + " name=" + global_destination_name);
547 final_destination = (vector)llList2String(full_journey, -1);
548 try_the_hard_cases();
550 //log_it("check hard case--already safe or decidedly unsafe, skipping.");
551 if (last_verification == VERIFY_SAFE) {
552 good_destinations_counted++;
553 if (!is_prefix(global_destination_name, QUADRANT_TAG_NAME)) {
554 ungenerated_good_destinations_counted++;
557 advance_reconnaissance_position();
559 take_the_next_appropriate_action();
560 } else if (actyo == AQ_ACQUIRE_BASE_PATH) {
561 // a response with a hopefully good destination arrived; base a path on that.
562 scramble_to_find_a_way(prechewed_destinations(llList2String(split, 0)));
566 // looks at the location "look_here" and finds the ground height as the z component
567 // of the returned vector (where x and y are copied from "look_here"). if the
568 // "height_add_in" is non-zero, it is added to the z component to adjust the ground
569 // height by an offset.
570 vector adjust_to_ground(vector look_here, float height_add_in)
572 float height = llGround(look_here - llGetPos());
573 look_here.z = height + height_add_in;
577 // tries to come up with a random path that can reach a difficult place.
578 scramble_to_find_a_way(list possible_helpful_path)
580 // we were told this was a good pathway, so let's try it.
581 if (randomize_within_range(0.0, 1.0, FALSE) > 0.5) {
582 // if we feel like it, add some huge random vectors in.
584 integer how_many = (integer)randomize_within_range(1, 5, FALSE);
585 //magic constant above.
586 vector rando = llGetPos(); // start with current place.
587 for (indy = 0; indy < how_many; indy++) {
588 // try to add a random completely other place in the sim.
589 vector other_rando = adjust_to_ground(random_vector(0.0, 256.0, FALSE), 64);
590 rando.z = other_rando.z;
591 // by only changing one axis at a time for x and y, we kind of slide around.
592 // this is considered more beneficial than just jumping randomly.
593 if (randomize_within_range(0.0, 1.0, FALSE) > 0.5) {
594 // this 50% modulates the x.
595 rando.x = other_rando.x;
597 // this other 50% modulates the y.
598 rando.y = other_rando.y;
600 //log_it("scramble: wacky rando: " + vector_chop(rando));
601 possible_helpful_path += [ vector_chop(rando) ];
604 if (randomize_within_range(0.0, 1.0, FALSE) > 0.2) {
605 // maybe we also feel like adding a small amount to the final destination,
606 // trying for a hook shot, most of the time.
607 //log_it("scramble: addition relative to final destination.");
608 vector rando = adjust_to_ground(final_destination + random_vector(0.0, 8.0, TRUE), 64);
609 //magic constant above.
610 possible_helpful_path += [ vector_chop(rando) ];
612 if ( (final_destination.z < 1000.0) && (randomize_within_range(0.0, 1.0, FALSE) > 0.05) ) {
613 //hmmm: is this redundant now?
614 // we very often add a vertical component so as to swoop down on the destination
615 // and hopefully avoid ground fault conditions.
616 possible_helpful_path += [ vector_chop(<final_destination.x, final_destination.y,
617 final_destination.z + 64>) ];
618 //magic constant above.
620 // now finally we add in the real place where we're going.
621 possible_helpful_path += [ vector_chop(final_destination) ];
622 // prepare our state for the new attempt.
623 is_recon_pending = TRUE;
625 full_journey = possible_helpful_path;
626 //log_it("scramble: final guess=" + (string)full_journey);
627 // rez for this nutty test path.
628 rez_a_jaunter(global_object_name,
629 RECONNAISSANCE_TRICKY_PARM + private_chat_channel, RECONNAISSANCE_TRIP);
632 // this function returns TRUE if we are close enough to the "destination".
633 integer close_enough(vector destination)
635 float PROXIMITY_REQUIRED = 0.1;
636 // how close we must be to the target location to call it done.
637 // matches current jaunting library proximity.
638 return (llVecDist(llGetPos(), destination) <= PROXIMITY_REQUIRED);
641 // records a verification state locally so we can make decisions during testing.
642 splice_just_verification(integer index, integer verif_state)
644 if ( (index < 0) || (index >= destinations_total) ) return; // can't do it.
645 global_verifications = chop_list(global_verifications, 0, index - 1)
647 + chop_list(global_verifications, index + 1, destinations_total - 1);
650 // replaces an existing entry at the "index" if known (otherwise this should be negative)
651 // for the location named "dest_name" if known (otherwise should be a restatement of the
652 // numerical index with '#' in front).
653 splice_destination_info(integer index, string dest_name, string pathway, integer verif)
655 //log_it("splicedest: " + (string)index + " name=" + dest_name + " verif=" + (string)verif + " path=" + pathway);
656 splice_just_verification(index, verif);
657 string new_entry = wrap_item_list([pathway, verif]);
658 llMessageLinked(LINK_THIS, DATA_COW_HUFFWARE_ID, ADD_ITEM_COMMAND,
659 wrap_parameters([dest_name, new_entry]));
662 string verification_name(integer enumtype)
664 if (enumtype == VERIFY_SAFE) return "ok";
665 else if (enumtype == VERIFY_UNSAFE_SO_FAR) return "uhh";
666 else if (enumtype == VERIFY_UNSAFE_DECIDED) return "far";
667 // catch-all, including untested.
671 // returns true if the slackness counter awaiting things has elapsed.
672 integer check_for_child_timeout()
674 if (snooze_counter++ > MAXIMUM_SNOOZES) {
675 // go back to the main state. we took too long.
676 // log_it("timed out!");
677 llUnSit(llAvatarOnSitTarget()); // don't hang onto the avatar for this error.
685 // action queue for postponed activities. the first field held in a list item here
686 // is an integer action code. the format of the remaining parameters is up to the
687 // caller, and they can be used as the final parameters for when the queued action
691 // looks at the action code at the head of the queue without removing the action.
692 integer peek_action_code()
694 list fields = llParseString2List(llList2String(action_queue, 0), [HUFFWARE_PARM_SEPARATOR], []);
695 return extract_action_code(fields);
698 // extracts the action code from a retrieved list.
699 integer extract_action_code(list to_parse) { return llList2Integer(to_parse, 0); }
701 // removes the current head of the action queue and returns it.
702 list pop_action_record()
704 list top_action = llParseString2List(llList2String(action_queue, 0), [HUFFWARE_PARM_SEPARATOR], []);
705 action_queue = llDeleteSubList(action_queue, 0, 0);
709 // adds a new action to the end of the action queue.
710 push_action_record(integer action, list added_parms)
712 snooze_counter = 0; // reset back for new action.
713 action_queue += [ wrap_parameters([action] + added_parms) ];
718 // processes link messages received from support libraries.
719 handle_link_message(integer which, integer num, string msg, string id)
721 // maybe it's a piece of data from the data cow.
722 if (num == DATA_COW_HUFFWARE_ID + REPLY_DISTANCE) {
723 // unpack the parameters we were given.
724 list parms_x = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
725 answer_to_the_cow(msg, id, parms_x);
729 if (num != JAUNT_REZOLATOR_HUFFWARE_ID) return; // not interesting.
730 if (DEBUGGING) log_it("linkmsg: msg " + msg + " id " + id);
731 list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
732 if (msg == REZ_CHILD_NOW) {
734 snooze_counter = 0; // reset counter.
735 if (llGetListLength(parms) < REZ_KID_FIELDS_NEEDED) {
736 // log_it("insufficient parameters for rezzing child.");
739 rez_requested_child(llList2String(parms, 0), llList2Integer(parms, 1),
740 llList2Integer(parms, 2), llList2String(parms, 3),
741 llList2Integer(parms, 4), llList2String(parms, 5));
742 } else if (msg == REPORT_CHILD_REZZED) {
744 } else if (msg == RESET_REZOLATOR) {
746 } else if (msg == REZOLATOR_CHILD_SUPPORT) {
747 provide_child_support(llList2Integer(parms, 0));
748 } else if (msg == REZOLATOR_CHILD_RETURNED) {
749 if (conveyance_mode == RECONNAISSANCE_TRIP) {
750 // unpack the success value from jaunter's check.
751 integer succeeded_recon = llList2Integer(parms, 0);
752 // report on our success.
753 list report_loc = full_journey;
754 if (!succeeded_recon) {
755 // we did at least get to somewhere, even if not to target.
756 report_loc = llParseString2List(llList2String(parms, 1),
757 [VECTOR_SEPARATOR], []);
759 llWhisper(private_chat_channel, CHILD_CHAT_TEXT + RETURN_WORD
760 + (string)succeeded_recon + DB_SEPARATOR
761 + global_destination_name
763 + llDumpList2String(report_loc, VECTOR_SEPARATOR));
764 //log_it("child reporting success=" + (string)succeeded_recon + " for " + global_destination_name + " via " + llDumpList2String(report_loc, VECTOR_SEPARATOR) );
766 //log_it("now dying at posn " + (string)llGetPos());
767 llDie(); // oh crud, we're toast.
770 if (!succeeded_recon) log_it("too far away; path okay?");
775 // makes our click action change to the type requested, and sends the request
776 // out to all our sub-prims also.
777 set_click_action(integer action)
779 llSetClickAction(action);
780 // secret message to other prims to change click action.
781 llMessageLinked(LINK_SET, CHANGE_CLICK_ACTION_HUFFWARE_ID, "", (string)action);
784 // tries to find a known safe locale that's close to the target. if there are any
785 // that are close, then we will not add this location. otherwise, we do add it,
786 // if the list is not already too large.
787 add_if_no_close_match(string pathway)
789 // make sure there's enough room to bother checking.
790 if (llGetListLength(safe_locales_seen) >= MAXIMUM_SAFE_LOCATIONS) return;
791 list dests = llParseString2List(pathway, [VECTOR_SEPARATOR], []);
792 vector final_locn = (vector)llList2String(dests, -1);
793 // set the z component to zero for this comparison, which is only about x,y position.
794 // since we then add this trimmed vector, we don't need to worry about the z component
795 // for the items already in the list.
798 for (i = 0; i < llGetListLength(safe_locales_seen); i++) {
799 vector curr = (vector)llList2String(safe_locales_seen, i);
800 if (llVecDist(curr, final_locn) < MINIMUM_DISTANCE_FOR_EXTRAS) {
801 //log_it("dest too close: dist = " + (string)llVecDist(curr, final_locn));
805 if (DEBUGGING) log_it("dest far enough to add: " + pathway);
806 // no known locale is close, so we'll add this one.
807 safe_locales_seen += [ final_locn ];
808 good_destinations_counted++;
809 add_destination(QUADRANT_TAG_NAME + "pt#" + (string)(partial_path_count++),
810 pathway, VERIFY_SAFE);
813 // process what we hear in open chat and on our special channels.
814 // this function should always return FALSE unless it's been given enough info
815 // to enter a new state, in which case it should return true.
816 listen_to_voices(integer channel, string name, key id, string message)
818 // check to see if the text is right.
820 log_it("heard: " + message + " from " + name);
821 if (!is_prefix(message, CHILD_CHAT_TEXT)) return; // not right.
822 message = llDeleteSubString(message, 0, llStringLength(CHILD_CHAT_TEXT) - 1);
823 // maybe this is a startup request.
824 if (is_prefix(message, READY_TEXT) && serving_root) {
825 // yes, this is a request for a destination. we are going to fill the rezzed object
826 // now, so figure out what type of jaunter needs info. formerly fill_rezzed_object.
827 integer is_recon = (integer)llGetSubString(message, -1, -1);
828 if (done_reconnoiter && is_recon) return; // we already did all that.
830 // a recon jaunter needs its instructions.
832 log_it("telling recon kid: name " + global_destination_name + " journ=" + (string)full_journey);
833 is_recon_pending = TRUE;
834 integer indy; // i told you N times.
835 for (indy = 0; indy < MAX_CHAT_REPEAT; indy++) {
836 llSay(private_chat_channel, CHILD_CHAT_TEXT
837 + global_destination_name
839 + llDumpList2String(full_journey, VECTOR_SEPARATOR));
840 // snooze to make sure we pause a little between mouthings.
841 llSleep(SLEEPY_TIME);
844 snooze_counter = 0; // starting over.
845 child_needs_setup = FALSE; // count off the one we just set up.
848 log_it("into code for one way jaunter");
849 // simple one way jaunter needs fillin'.
850 list added_parms = [];
851 push_action_record(AQ_ONEWAY_JAUNTER_VOYAGE, added_parms);
852 llMessageLinked(LINK_THIS, DATA_COW_HUFFWARE_ID, TAGGED_GET_ITEM_COMMAND,
853 wrap_parameters([OUR_COW_TAG, global_destination_name]));
858 if (DEBUGGING) log_it("into kid side of rezolator...");
861 if ( (conveyance_mode == RECONNAISSANCE_TRIP) || (conveyance_mode == ONE_WAY_TRIP) ) {
862 if (heard_from_root) return; // we already did this bit.
863 // this jaunter is destined for short-lived work, but now at least has a goal.
864 // we just pass along the parameters that were told to us from the root jaunter.
865 list parms = llParseString2List(message, [DB_SEPARATOR], []);
866 global_destination_name = llList2String(parms, 0);
867 full_journey = llParseString2List(llList2String(parms, 1), [VECTOR_SEPARATOR], []);
868 final_destination = (vector)llList2String(full_journey, -1);
869 parms += [ conveyance_mode ];
871 if (conveyance_mode == ONE_WAY_TRIP) {
872 // set the click action properly.
873 if (outside_of_sim(final_destination)) set_click_action(CLICK_ACTION_TOUCH);
874 else set_click_action(CLICK_ACTION_SIT);
877 if (DEBUGGING) log_it("kid heard go to: " + global_destination_name + " via " + (string)full_journey);
878 llMessageLinked(LINK_THIS, JAUNT_REZOLATOR_HUFFWARE_ID + REPLY_DISTANCE,
879 REZOLATOR_EVENT_GOT_INSTRUCTIONS, wrap_parameters(parms));
880 // make sure we cancel the listening so we don't hear directions that aren't for us.
881 llListenRemove(private_id_from_listen);
882 heard_from_root = TRUE;
883 return; // only leave for recon.
886 // this jaunter's role should be a root jaunter, and we have a reply about a recon mission.
887 is_recon_pending = FALSE; // we got an acceptable answer.
888 list parms = llParseString2List(llDeleteSubString
889 (message, 0, llStringLength(RETURN_WORD) - 1), [DB_SEPARATOR], []);
890 integer success = llList2Integer(parms, 0);
891 string dest_name = llList2String(parms, 1);
892 string pathway = llList2String(parms, 2);
893 //log_it("|||| recon reply: " + dest_name + " good=" + (string)success + " path=" + pathway);
895 // record this as a safe pathway.
896 last_verification = VERIFY_SAFE;
897 good_destinations_counted++;
899 // a failure is being reported. record this as a bad pathway.
900 last_verification = VERIFY_UNSAFE_SO_FAR;
901 // however, the jaunter should have told us where it did get to. record that whole
902 // thing if it seems like a good distance from our other known good places.
903 if (DEBUGGING) log_it("failed path did get to: " + pathway);
904 add_if_no_close_match(pathway);
907 // only save this info if we're not working on recons.
908 if (!trying_hard_cases || success) {
909 // fix name for tough destination's real one.
910 if (trying_hard_cases) {
911 dest_name = "#" + (string)current_recon_index;
913 splice_destination_info(current_recon_index, dest_name,
914 llDumpList2String(full_journey,
915 ///llList2List(full_journey, 1, -1),
919 take_the_next_appropriate_action();
923 // processes a destination set to handle special cases, like for offset jaunting.
924 list prechewed_destinations(string dests)
927 //hmmm: document this! as in, we support the offset format.
929 // look for our special start character for offsets.
930 if (is_prefix(dests, "o"))
931 // if this is an offset version, then chop whatever word they used starting with 'o'
932 // and compute the destination based on current position.
933 return [ (vector)llDeleteSubString(dests, 0, find_substring(dests, "<") - 1) + llGetPos() ];
935 // normal jaunt to absolute coordinates.
936 return llParseString2List(dests, [VECTOR_SEPARATOR], []);
939 // creates a jaunter object given which inventory item and the startup parm.
940 rez_a_jaunter(string object_name, integer rez_parm, integer conveyance_mode)
942 // the minimum and maximum distances for placing the auto-rezzed jaunter object.
943 integer MIN_REZ_DISTANCE = 1;
944 integer MAX_REZ_DISTANCE = 3;
945 vector RECON_ROTATION = <270.0, 0.0, 0.0>;
946 // what angle we use when planting the recon jaunter.
947 // this is tightly coupled to our particular object.
948 vector point_at_jaunter;
949 if (conveyance_mode == RECONNAISSANCE_TRIP) {
950 // we just use our specified rotation.
951 point_at_jaunter = RECON_ROTATION * DEG_TO_RAD;
952 // start at where the jaunter will be when it tries the same path.
953 global_rez_place = llGetPos();
955 // we randomize the location to avoid people having the objects
956 // in a big pile on top of each other.
957 global_rez_place = llGetPos()
958 + <randomize_within_range(MIN_REZ_DISTANCE, MAX_REZ_DISTANCE, TRUE),
959 randomize_within_range(MIN_REZ_DISTANCE, MAX_REZ_DISTANCE, TRUE),
960 0.4>; // pop it up a bit.
961 // aim the temporary ride at the root jaunter.
962 vector aim_back = llGetPos() - global_rez_place;
963 float z_rot = llAtan2(aim_back.y, aim_back.x);
964 // calculate the vector for rotation given the z rotation.
965 point_at_jaunter = <0.0, 0.0, z_rot>;
967 llRezObject(object_name, global_rez_place,
968 ZERO_VECTOR, llEuler2Rot(point_at_jaunter), rez_parm);
969 if (conveyance_mode != RECONNAISSANCE_TRIP) {
970 // this model only jaunts to where it gave out the object. that
971 // should hopefully always work. but at least if it can't get back,
972 // it's really close by.
973 full_journey = [ vector_chop(llGetPos()), vector_chop(global_rez_place) ];
975 child_needs_setup = TRUE; // we aren't ready to go even if jaunt is done.
976 snooze_counter = 0; // amnesty on the timer.
979 // finds a pathway that's already in our list and that we think is safe.
980 integer load_random_safe_path()
983 integer goodunz = good_destinations_counted;
985 // now that we know how many are safe, we can pick a good destination.
986 integer randola = llRound(randomize_within_range(0, goodunz - 1, FALSE));
987 integer safe_indy = -1; // tracks which safe item we're at.
988 for (indy = 0; indy < llGetListLength(global_verifications); indy++) {
989 // scoot across the list to find the Nth safe one.
990 if (llList2Integer(global_verifications, indy) == VERIFY_SAFE) {
991 if (safe_indy == randola) {
992 //log_it("like path at " + (string)safe_indy);
993 // we're at the Nth safe item, so this is our random choice.
994 list added_parms = [];
995 push_action_record(AQ_ACQUIRE_BASE_PATH, added_parms);
996 llMessageLinked(LINK_THIS, DATA_COW_HUFFWARE_ID, TAGGED_GET_ITEM_COMMAND,
997 wrap_parameters([OUR_COW_TAG, "#" + (string)safe_indy]));
1000 // increment to the next position, since we've seen this one.
1004 //log_it("found no good path to use");
1008 // processes the timer events during an ongoing rezzing process.
1011 // this is the root's timer process, which is implemented in great detail
1012 // in the function below.
1013 if (serving_root) take_the_next_appropriate_action();
1016 // file a report with the root jaunter that the child has been created and is
1017 // ready for service.
1020 // tell the root jaunter know that we're ready to be packed for our trip.
1021 llSay(private_chat_channel, CHILD_CHAT_TEXT + READY_TEXT
1022 + (string)(conveyance_mode == RECONNAISSANCE_TRIP));
1025 // plops a new destination on the end of the lists.
1026 add_destination(string name, string path, integer verif)
1028 //log_it("adding " + name + " with path " + path + " and verif=" + (string)verif);
1029 global_verifications += [ verif ];
1030 // send our new information to the data cow. we store the information encoded as
1031 // two separated items, where the first element is the destination and the second
1032 // is the verification state.
1033 string new_entry = wrap_item_list([path, verif]);
1034 llMessageLinked(LINK_THIS, DATA_COW_HUFFWARE_ID, ADD_ITEM_COMMAND,
1035 wrap_parameters([name, new_entry]));
1036 destinations_total++; // we just added another destination.
1042 integer debug_num = 0;
1044 // a debugging output method. can be disabled entirely in one place.
1045 log_it(string to_say)
1048 // tell this to the owner.
1049 llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
1050 // say this on open chat, but use an unusual channel.
1051 // llSay(108, (string)debug_num + "- " + to_say);
1054 // returns TRUE if the value in "to_check" specifies a legal x or y value in a sim.
1055 integer valid_sim_value(float to_check)
1057 if (to_check < 0.0) return FALSE;
1058 if (to_check >= 257.0) return FALSE;
1062 // returns TRUE if the "to_check" vector is a location outside of the current sim.
1063 integer outside_of_sim(vector to_check)
1065 return !valid_sim_value(to_check.x) || !valid_sim_value(to_check.y);
1068 // returns text for a floating point number, but includes only
1069 // two digits after the decimal point.
1070 string float_chop(float to_show)
1072 integer mant = llAbs(llRound(to_show * 100.0) / 100);
1074 if (to_show < 0.0) neg_sign = "-";
1075 string dec_s = (string)((llRound(to_show * 100.0) - mant * 100) / 100.0);
1076 dec_s = llGetSubString(llGetSubString(dec_s, find_substring(dec_s, ".") + 1, -1), 0, 2);
1077 // strip off all trailing zeros.
1078 while (llGetSubString(dec_s, -1, -1) == "0")
1079 dec_s = llDeleteSubString(dec_s, -1, -1);
1080 string to_return = neg_sign + (string)mant;
1081 if (llStringLength(dec_s)) to_return += "." + dec_s;
1085 // returns a prettier form for vector text, with chopped floats.
1086 string vector_chop(vector to_show)
1088 return "<" + float_chop(to_show.x) + ","
1089 + float_chop(to_show.y) + ","
1090 + float_chop(to_show.z) + ">";
1093 // joins a list of parameters using the parameter sentinel for the library.
1094 string wrap_parameters(list to_flatten)
1095 { return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); }
1097 // joins a list of sub-items using the item sentinel for the library.
1098 string wrap_item_list(list to_wrap)
1099 { return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); }
1101 // returns a number at most "maximum" and at least "minimum".
1102 // if "allow_negative" is TRUE, then the return may be positive or negative.
1103 float randomize_within_range(float minimum, float maximum, integer allow_negative)
1105 if (minimum > maximum) {
1106 // flip the two if they are reversed.
1107 float temp = minimum; minimum = maximum; maximum = temp;
1109 float to_return = minimum + llFrand(maximum - minimum);
1110 if (allow_negative) {
1111 if (llFrand(1.0) < 0.5) to_return *= -1.0;
1116 // returns a random vector where x,y,z will be between "minimums" and "maximums"
1117 // x,y,z components. if "allow_negative" is true, then any component will
1118 // randomly be negative or positive.
1119 vector random_bound_vector(vector minimums, vector maximums, integer allow_negative)
1121 return <randomize_within_range(minimums.x, maximums.x, allow_negative),
1122 randomize_within_range(minimums.y, maximums.y, allow_negative),
1123 randomize_within_range(minimums.z, maximums.z, allow_negative)>;
1126 // returns a vector whose components are between minimum and maximum.
1127 // if allow_negative is true, then they can be either positive or negative.
1128 vector random_vector(float minimum, float maximum, integer allow_negative)
1130 return random_bound_vector(<minimum, minimum, minimum>,
1131 <maximum, maximum, maximum>, allow_negative);
1134 // returns the portion of the list between start and end, but only if they are
1135 // valid compared with the list length. an attempt to use negative start or
1136 // end values also returns a blank list.
1137 list chop_list(list to_chop, integer start, integer end)
1139 integer last_len = llGetListLength(to_chop) - 1;
1140 if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return [];
1141 return llList2List(to_chop, start, end);
1144 // returns the index of the first occurrence of "pattern" inside
1145 // the "full_string". if it is not found, then a negative number is returned.
1146 integer find_substring(string full_string, string pattern)
1148 string full_lower = llToLower(full_string);
1149 return llSubStringIndex(full_lower, pattern);
1152 // returns TRUE if the "prefix" string is the first part of "compare_with".
1153 integer is_prefix(string compare_with, string prefix)
1154 { return find_substring(compare_with, prefix) == 0; }
1157 // huffware script: auto-retire, by fred huffhines, version 2.8.
1158 // distributed under BSD-like license.
1159 // !! keep in mind that this code must be *copied* into another
1160 // !! script that you wish to add auto-retirement capability to.
1161 // when a script has auto_retire in it, it can be dropped into an
1162 // object and the most recent version of the script will destroy
1163 // all older versions.
1165 // the version numbers are embedded into the script names themselves.
1166 // the notation for versions uses a letter 'v', followed by two numbers
1167 // in the form "major.minor".
1168 // major and minor versions are implicitly considered as a floating point
1169 // number that increases with each newer version of the script. thus,
1170 // "hazmap v0.1" might be the first script in the "hazmap" script continuum,
1171 // and "hazmap v3.2" is a more recent version.
1173 // example usage of the auto-retirement script:
1176 // auto_retire(); // make sure newest addition is only version of script.
1179 // this script is partly based on the self-upgrading scripts from markov brodsky
1180 // and jippen faddoul.
1183 string self = llGetScriptName(); // the name of this script.
1184 list split = compute_basename_and_version(self);
1185 if (llGetListLength(split) != 2) return; // nothing to do for this script.
1186 string basename = llList2String(split, 0); // script name with no version attached.
1187 string version_string = llList2String(split, 1); // the version found.
1189 // find any scripts that match the basename. they are variants of this script.
1190 for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
1191 string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
1192 if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
1193 // found a basic match at least.
1194 list inv_split = compute_basename_and_version(curr_script);
1195 if (llGetListLength(inv_split) == 2) {
1196 // see if this script is more ancient.
1197 string inv_version_string = llList2String(inv_split, 1); // the version found.
1198 // must make sure that the retiring script is completely the identical basename;
1199 // just matching in the front doesn't make it a relative.
1200 if ( (llList2String(inv_split, 0) == basename)
1201 && ((float)inv_version_string < (float)version_string) ) {
1202 // remove script with same name from inventory that has inferior version.
1203 llRemoveInventory(curr_script);
1210 // separates the base script name and version number. used by auto_retire.
1211 list compute_basename_and_version(string to_chop_up)
1213 // minimum script name is 2 characters plus a version.
1214 integer space_v_posn;
1215 // find the last useful space and 'v' combo.
1216 for (space_v_posn = llStringLength(to_chop_up) - 3;
1217 (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
1219 // look for space and v but do nothing else.
1221 if (space_v_posn < 2) return []; // no space found.
1222 // now we zoom through the stuff after our beloved v character and find any evil
1223 // space characters, which are most likely from SL having found a duplicate item
1224 // name and not so helpfully renamed it for us.
1226 for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
1227 if (llGetSubString(to_chop_up, indy, indy) == " ") {
1228 // found one; zap it. since we're going backwards we don't need to
1229 // adjust the loop at all.
1230 to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
1233 string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
1234 // ditch the space character for our numerical check.
1235 string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
1236 // strip out a 'v' if there is one.
1237 if (llGetSubString(chop_suffix, 0, 0) == "v")
1238 chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
1239 // if valid floating point number and greater than zero, that works for our version.
1240 string basename = to_chop_up; // script name with no version attached.
1241 if ((float)chop_suffix > 0.0) {
1242 // this is a big success right here.
1243 basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
1244 return [ basename, chop_suffix ];
1246 // seems like we found nothing useful.
1257 state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
1258 on_rez(integer parm) { state rerun; }
1260 state rerun { state_entry() { state default; } }
1264 state_entry() { auto_retire(); initialize_rezolator(); }
1266 on_rez(integer startup) { llResetScript(); }
1268 state_exit() { llSetTimerEvent(0.0); }
1270 link_message(integer which, integer num, string msg, key id)
1271 { handle_link_message(which, num, msg, id); }
1273 listen(integer channel, string name, key id, string message)
1274 { listen_to_voices(channel, name, id, message); }
1276 timer() { handle_timer(); }