From dc3a7c140ef0a059f3b405bac0cf22791cac0607 Mon Sep 17 00:00:00 2001 From: Chris Koeritz Date: Mon, 30 Apr 2012 12:59:57 -0400 Subject: [PATCH] more code committed for LSL huffware, basically dropping the freebie updater version in favor of the "real" eepaw updater. --- .../FPS_color_changer_v2.4.lsl | 0 .../FreeView_v1.8.lsl | 0 .../JukeBox_v1.6.lsl | 0 .../TL_Door_fredmod_v4.4.lsl | 0 .../a_huffotronic_update_server_v23.2.lsl | 0 .../anti-idler_mojo_v1.4.lsl | 189 +++ .../auto-retire_v2.8.lsl | 0 .../card_configurator_v8.1.lsl | 0 .../change_click_action_v1.0.lsl | 151 ++ .../clear_text_and_effects_v2.2.lsl | 0 .../color_smoove_clock_v3.2.lsl | 256 ++++ .../comfortable_sitting_v7.5.lsl | 0 .../concussive_v19.2.lsl | 487 +++++++ .../cowpole_ignite_v1.2.lsl | 129 ++ .../create_objects_v1.3.lsl | 0 .../data_cow_v3.3.lsl | 0 .../docs_for_searchbert_v2.2.lsl | 76 + .../eepaw_shop_note_v0.8.lsl | 20 + .../fade_opacity_v3.7.lsl | 0 .../fade_prim_v3.2.lsl | 158 +++ .../fredboxmux_no_rot_v3.0.lsl | 0 .../fredboxmux_v3.0.lsl | 0 .../giftorse_v7.6.lsl | 451 ++++++ .../huff-pet_v18.6.lsl | 963 +++++++++++++ .../huff-search_brainiac_v46.7.lsl | 1248 +++++++++++++++++ .../huff-search_pointer_v10.0.lsl | 574 ++++++++ .../huff-update_client_v20.1.lsl | 0 .../huffbee_bulb_v4.7.lsl | 252 ++++ .../hufflets_v6.3.lsl | 0 .../huffware_id_registry_v3.2.lsl | 0 .../inventory_exchanger_v3.7.lsl | 0 .../invoke_nechung_v1.2.lsl | 135 ++ .../jaunting_library_v15.9.lsl | 551 ++++++++ .../jump_good_v6.0.lsl | 318 +++++ ..._osgrid_&_personal_opensims_only_v1.9.txt} | 2 +- ...license__freebie_in_the_metaverse_v1.8.txt | 20 + .../mediatron_control_v7.2.lsl | 727 ++++++++++ .../menu_list_manager_v12.4.lsl | 0 .../menutini_library_v5.9.lsl | 0 .../minnow_v10.6.lsl | 395 ++++++ .../non-script_giver_v2.8.lsl | 0 .../noteworthy_library_v12.4.lsl | 0 .../particle_projector_v3.1.lsl | 0 .../party_culiar_v5.7.lsl | 0 .../phantomizer_v1.6.lsl | 165 +++ .../puffer_v1.3.lsl | 186 +++ .../radio_tuner_v1.2.lsl | 174 +++ .../rezzeroni_v20.2.lsl | 1061 ++++++++++++++ .../rotanium_rotato_v2.7.lsl | 0 .../searchbert_armature_v15.3.lsl | 492 +++++++ .../searchbert_config_v0.4.lsl | 22 + .../searchbert_menus_v4.6.lsl | 505 +++++++ .../set_comparator_library_v4.1.lsl | 554 ++++++++ .../slate_reader_v1.2.lsl | 363 +++++ .../star_chair_sitting_config_v0.4.lsl | 11 + .../take_touches_v0.2.lsl | 0 .../text_label_v3.9.lsl | 0 .../texture_mover_v3.0.lsl | 0 .../texture_shower_v2.7.lsl | 0 .../viewscreen_blitter_v3.9.lsl | 205 +++ .../weapon_fire_v2.2.lsl | 219 +++ .../welcomebot_v4.0.lsl | 529 +++++++ .../wylie_controller_v8.4.lsl | 624 +++++++++ .../zap_updater_from_elevators_v1.3.lsl | 187 +++ .../zenmondos_mailbox_v0.4.lsl | 0 65 files changed, 12398 insertions(+), 1 deletion(-) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/FPS_color_changer_v2.4.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/FreeView_v1.8.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/JukeBox_v1.6.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/TL_Door_fredmod_v4.4.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/a_huffotronic_update_server_v23.2.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/anti-idler_mojo_v1.4.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/auto-retire_v2.8.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/card_configurator_v8.1.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/change_click_action_v1.0.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/clear_text_and_effects_v2.2.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/color_smoove_clock_v3.2.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/comfortable_sitting_v7.5.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/concussive_v19.2.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/cowpole_ignite_v1.2.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/create_objects_v1.3.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/data_cow_v3.3.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/docs_for_searchbert_v2.2.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/eepaw_shop_note_v0.8.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/fade_opacity_v3.7.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/fade_prim_v3.2.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/fredboxmux_no_rot_v3.0.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/fredboxmux_v3.0.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/giftorse_v7.6.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/huff-pet_v18.6.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/huff-search_brainiac_v46.7.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/huff-search_pointer_v10.0.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/huff-update_client_v20.1.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/huffbee_bulb_v4.7.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/hufflets_v6.3.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/huffware_id_registry_v3.2.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/inventory_exchanger_v3.7.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/invoke_nechung_v1.2.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/jaunting_library_v15.9.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/jump_good_v6.0.lsl rename huffware/{huffotronic_updater_freebie_v5.3/license__free_in_osgrid_only_v1.8.lsl => huffotronic_eepaw_knowledge_v60.9/license__free_in_osgrid_&_personal_opensims_only_v1.9.txt} (94%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/license__freebie_in_the_metaverse_v1.8.txt create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/mediatron_control_v7.2.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/menu_list_manager_v12.4.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/menutini_library_v5.9.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/minnow_v10.6.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/non-script_giver_v2.8.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/noteworthy_library_v12.4.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/particle_projector_v3.1.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/party_culiar_v5.7.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/phantomizer_v1.6.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/puffer_v1.3.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/radio_tuner_v1.2.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/rezzeroni_v20.2.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/rotanium_rotato_v2.7.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_armature_v15.3.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_config_v0.4.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_menus_v4.6.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/set_comparator_library_v4.1.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/slate_reader_v1.2.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/star_chair_sitting_config_v0.4.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/take_touches_v0.2.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/text_label_v3.9.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/texture_mover_v3.0.lsl (100%) rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/texture_shower_v2.7.lsl (100%) create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/viewscreen_blitter_v3.9.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/weapon_fire_v2.2.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/welcomebot_v4.0.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/wylie_controller_v8.4.lsl create mode 100755 huffware/huffotronic_eepaw_knowledge_v60.9/zap_updater_from_elevators_v1.3.lsl rename huffware/{huffotronic_updater_freebie_v5.3 => huffotronic_eepaw_knowledge_v60.9}/zenmondos_mailbox_v0.4.lsl (100%) diff --git a/huffware/huffotronic_updater_freebie_v5.3/FPS_color_changer_v2.4.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/FPS_color_changer_v2.4.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/FPS_color_changer_v2.4.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/FPS_color_changer_v2.4.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/FreeView_v1.8.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/FreeView_v1.8.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/FreeView_v1.8.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/FreeView_v1.8.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/JukeBox_v1.6.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/JukeBox_v1.6.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/JukeBox_v1.6.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/JukeBox_v1.6.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/TL_Door_fredmod_v4.4.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/TL_Door_fredmod_v4.4.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/TL_Door_fredmod_v4.4.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/TL_Door_fredmod_v4.4.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/a_huffotronic_update_server_v23.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/a_huffotronic_update_server_v23.2.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/a_huffotronic_update_server_v23.2.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/a_huffotronic_update_server_v23.2.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/anti-idler_mojo_v1.4.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/anti-idler_mojo_v1.4.lsl new file mode 100755 index 00000000..6a7b67cc --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/anti-idler_mojo_v1.4.lsl @@ -0,0 +1,189 @@ + +// huffware script: anti-idler mojo, by fred huffhines +// +// original version was by kwipper manimal. that script has been augmented with +// a better anti-idle animation and some other additions. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +float TIMER_INTERVAL = 28.0; // number of seconds between checks on avatar health. + +float JUMP_AROUND_INTERVAL = 241.0; // how often (in seconds) to move around a bit. + +float JUMP_AROUND_RUN_TIME = 4.0; // number of seconds to do the animation. + +integer last_jumped = 0; // when did we last jump jump jump around. + +integer animation_is_running = FALSE; // is there an animation playing right now? + +get_perms() +{ + llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION); + llSetTimerEvent(TIMER_INTERVAL); // always set a timer, so we can check on state. +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + get_perms(); + } + + changed(integer change){ + if (change & 128){ +//ugly use of constant + llResetScript(); + } + } + + attach(key id) { + if (id != NULL_KEY) { + get_perms(); + } + } + + run_time_permissions(integer perm) { + if (perm & PERMISSION_TRIGGER_ANIMATION) { +////not here. llSetTimerEvent(TIMER_INTERVAL); + // anything we do want to do here? + } + } + + timer() { + if (llGetPermissions() & PERMISSION_TRIGGER_ANIMATION) { + // turn off the away animation, just in case. + llStopAnimation("away"); + // were we doing an animation in the previous loop cycle? + if (animation_is_running) { + animation_is_running = FALSE; + llStopAnimation(llGetInventoryName(INVENTORY_ANIMATION, 0)); + // back to normal timing now. + llSetTimerEvent(TIMER_INTERVAL); + } + if (llAbs(last_jumped - llGetUnixTime()) >= JUMP_AROUND_INTERVAL) { + // time to jump around with our expected animation. + // hopefully this makes the avatar seem more awake to sl. + if (llGetInventoryNumber(INVENTORY_ANIMATION) > 0) { +//llOwnerSay("found animation to play: " + llGetInventoryName(INVENTORY_ANIMATION, 0)); + llStartAnimation(llGetInventoryName(INVENTORY_ANIMATION, 0)); + } + last_jumped = llGetUnixTime(); + animation_is_running = TRUE; + // only let the animation play for our specified timing. + llSetTimerEvent(JUMP_AROUND_RUN_TIME); + } + } else { +llOwnerSay("croaking in timer because we don't have animation permissions; resetting."); + llResetScript(); + } + } +} + diff --git a/huffware/huffotronic_updater_freebie_v5.3/auto-retire_v2.8.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/auto-retire_v2.8.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/auto-retire_v2.8.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/auto-retire_v2.8.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/card_configurator_v8.1.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/card_configurator_v8.1.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/card_configurator_v8.1.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/card_configurator_v8.1.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/change_click_action_v1.0.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/change_click_action_v1.0.lsl new file mode 100755 index 00000000..76cbc3aa --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/change_click_action_v1.0.lsl @@ -0,0 +1,151 @@ + +// huffware script: change click action, by fred huffhines. +// +// a simple script for sub-prims in an object where you want the whole object's +// click action to change. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// change click action's link message API... +////////////// +integer CHANGE_CLICK_ACTION_HUFFWARE_ID = 10024; + // a unique ID within the huffware system for this script. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +////////////// +// +// the API only supports the service number; there are no commands to pass in the "msg" +// parameter. the id does accept a parameter, which is the type of click action to begin +// using for this prim. +// +////////////// + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + } + + link_message(integer which, integer num, string msg, key id) + { + // we only allow this action change on sub-prims. + if ( (num == CHANGE_CLICK_ACTION_HUFFWARE_ID) && (llGetLinkNumber() != 1) ) { + llSetClickAction((integer)((string)id)); + } + } +} + diff --git a/huffware/huffotronic_updater_freebie_v5.3/clear_text_and_effects_v2.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/clear_text_and_effects_v2.2.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/clear_text_and_effects_v2.2.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/clear_text_and_effects_v2.2.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/color_smoove_clock_v3.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/color_smoove_clock_v3.2.lsl new file mode 100755 index 00000000..dc4b0293 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/color_smoove_clock_v3.2.lsl @@ -0,0 +1,256 @@ + +// huffware script: color smoove clock, by fred huffhines +// +// A clock with rotating color text, adjustable for your time zone. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// constants you might want to modify... + +integer TIME_ZONE = -5; + // the time zone you want the clock to display, measured as an offset from GMT. + +float TIMER_INTERVAL = 0.1; + // how frequently timer is hit to adjust color and possibly change time text. + +float MAX_RANDOM_COLOR_JUMP = 0.07; + // how much the color might change in one timer hit for one R/G/B component. + +// global variables... + +// remembers the last time string we printed, since we want to be modifying the +// colors more frequently than we're changing the clock. +string global_time_string = ""; + +// track how many ticks have hit the timer. only updates the time when a whole +// second has gone by. +integer global_tick_counter = 0; + +// the current color for clock text; the default is just a starting point. +vector global_text_color = <0.3, 0.6, 0.8>; + +// records whether the value is going up (positive) or down (negative). +float global_elevator_x = -1.0; +float global_elevator_y = 1.0; +float global_elevator_z = 1.0; + +// do not change the following constants... + +integer PST_TIME_ZONE = -8; // this constant for the pacific time zone's offset from GMT. + +// makes sure components of the color don't go out of range. if they do, then +// the elevator direction is reversed. +normalize_color() +{ + if (global_text_color.x > 1.0) { global_text_color.x = 1.0; global_elevator_x *= -1.0; } + if (global_text_color.x < 0.0) { global_text_color.x = 0.0; global_elevator_x *= -1.0; } + if (global_text_color.y > 1.0) { global_text_color.y = 1.0; global_elevator_y *= -1.0; } + if (global_text_color.y < 0.0) { global_text_color.y = 0.0; global_elevator_y *= -1.0; } + if (global_text_color.z > 1.0) { global_text_color.z = 1.0; global_elevator_z *= -1.0; } + if (global_text_color.z < 0.0) { global_text_color.z = 0.0; global_elevator_z *= -1.0; } +} + +// show the current time string in our current color. +display_colored_text() +{ + llSetText(global_time_string, global_text_color, 1.0); +} + +rotate_text_color() +{ + // gnarly version is totally random. merely an example now. +//yuck; flashy global_text_color = ; + + // better; rotates the colors as slow as you like, but somewhat randomly + // for an interesting color glide effect. + global_text_color.x += llFrand(MAX_RANDOM_COLOR_JUMP) * global_elevator_x; + global_text_color.y += llFrand(MAX_RANDOM_COLOR_JUMP) * global_elevator_y; + global_text_color.z += llFrand(MAX_RANDOM_COLOR_JUMP) * global_elevator_z; + + normalize_color(); // make sure we didn't go out of bounds. + + display_colored_text(); // update to the new color. +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + // state_entry() is an event handler, it executes whenever a state is entered. + state_entry() + { + auto_retire(); + llSetTimerEvent(TIMER_INTERVAL); + } + + timer() + { + // calculate our current rate of timer hits. + float ticks_per_second = 1.0 / TIMER_INTERVAL; + + rotate_text_color(); // spin the text color every timer hit. + + // check whether the clock time has changed since last updated. + global_tick_counter++; + if (global_tick_counter < ticks_per_second) { + return; + } + // yep, we need to update the text of the time. + global_tick_counter = 0; // reset for rollover. + + // get the number of seconds off of the clock on the wall... + integer full_seconds = llFloor(llGetWallclock()); + + // switch to the chosen time zone. + full_seconds += -(PST_TIME_ZONE - TIME_ZONE) * 60 * 60; + + // a constant for how many seconds exist in a day. + integer DAY_OF_SECONDS = 24 * 60 * 60; + + // correct any tendency of the seconds to be below zero or above the maximum + // due to wacky time zones. + while (full_seconds < 0) full_seconds += DAY_OF_SECONDS; + while (full_seconds > DAY_OF_SECONDS) full_seconds -= DAY_OF_SECONDS; + + // calculate all the components of the time. + integer seconds = full_seconds; + integer minutes = llFloor(seconds / 60); + seconds -= minutes * 60; + integer hour = llFloor(minutes / 60); + minutes -= hour * 60; +//llOwnerSay("fs=" + (string)full_seconds + " h=" + (string)hour + " m=" + (string)minutes); + + integer is_am = hour < 12; + if (hour > 12) hour -= 12; // convert down to 12 hour time. + if (hour == 0) hour = 12; // don't show a zero in 12 hour time. + + // prepare the time update message... + string minutes_text = (string)minutes; + if (minutes < 10) minutes_text = "0" + minutes_text; + string seconds_text = (string)seconds; + if (seconds < 10) seconds_text = "0" + seconds_text; + + string meridian_string = "am"; + if (!is_am) meridian_string = "pm"; + + // update our time string now that we know all the pieces. + global_time_string = (string)hour + ":" + minutes_text + ":" + + seconds_text + meridian_string; + // flip the text up above the object to show the time. + display_colored_text(); + } + +} + +//credits: +// parts from lsl wiki originally? +// Enables an object to display the time for the time zone of your choosing. +// Thanks to Ben Linden for insight into timers, time and text setting. diff --git a/huffware/huffotronic_updater_freebie_v5.3/comfortable_sitting_v7.5.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/comfortable_sitting_v7.5.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/comfortable_sitting_v7.5.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/comfortable_sitting_v7.5.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/concussive_v19.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/concussive_v19.2.lsl new file mode 100755 index 00000000..d658d55e --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/concussive_v19.2.lsl @@ -0,0 +1,487 @@ + +// huffware script: concussive (aka blow em up), by fred huffhines +// +// provides a script for a missile that will start tracking the last position it +// hit something at and will start a sensor probe to see if there are avatars there. +// if there are, it will blow up and try to take avatars and active objects with it. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// hmmm: unimplemented ideas... +// buddy list; ids to not blow up besides the owner. +// maybe that's just a team setup. +// probably better in a notecard? + + +// configurable parameters... + +float sense_distance = 23.0; + // the distance from the bisconation device within which to search for happy targets. + +float sensory_addition = 14.0; + // the amount of distance that can be added in the case of + // a failure to sense any avatars or items. + +float push_magnitude = 2147483646.0; //maxint - 1, dealing with svc-2723. + // how much to push the targets that we have located. + +integer MAXIMUM_HITS = 2; + // the most times the object is allowed through its sensor loops. + +float MAX_VEL_ADJUST = 20.0; + // the maximum amount we would randomly add to the object's velocity after a + // failure to sense any items. + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +//borrowed from jump good +set_velocity(vector new_velocity, integer local_axis) +{ + vector current_velocity = llGetVel(); + + if (local_axis) { + rotation rot = llGetRot(); + current_velocity /= rot; // undo the rotation. + } + + new_velocity -= current_velocity; + new_velocity *= llGetMass(); + + llApplyImpulse(new_velocity, local_axis); +} + +// attempts to bring the object to a complete stop. +full_stop() +{ + llSetForce(<0,0,0>, FALSE); + set_velocity(<0,0,0>, FALSE); +} + +////////////// + +////////////// +//borrowed from jaunt script... +/////////////// + +// returns the portion of the list between start and end, but only if they are valid compared with +// the list length. an attempt to use negative start or end values also returns a blank list. +list chop_list(list to_chop, integer start, integer end) +{ + integer last_len = llGetListLength(to_chop) - 1; + if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return []; + return llList2List(to_chop, start, end); +} + +// the most jumps the script will try to take. the overall distance from the start +// to the end can be 10 * MAXIMUM_JUMPS meters. +integer MAXIMUM_JUMPS = 100; + +// used to calculate jump distances. +vector last_posn; + +// helper function for warp_across_list. this adds one jump vector to the +// list of rules as long as the destination is interesting. +list process_warp_item(vector next_jump) +{ +//tell_owner("last posn " + (string)last_posn); +//tell_owner("next warp " + (string)next_jump); + // calculate the number of jumps needed. + integer jumps = (integer)(llVecDist(next_jump, last_posn) / 10.0) + 1; + // calculate the offset needed for crossing sim boundaries. +/// adjust_offset(next_jump); + last_posn = next_jump; // record for next check. + // set up our list which we'll replicate. + list rules = [ PRIM_POSITION, next_jump ]; + // Try and avoid stack/heap collisions. + if (jumps > MAXIMUM_JUMPS) jumps = MAXIMUM_JUMPS; + // add the rules repeatedly to get the effective overall jump done. + integer count = 1; + while ( (count = count << 1) < jumps) rules += rules; + // magnify the rule list before really adding it. this gets us to the proper + // final number of jumps. + return rules + llList2List(rules, (count - jumps) << 1, count); +} + +// originally based on warpPos from lsl wiki but drastically modified. +// uses a set of +// transfer points instead of a single target. +list warp_across_list(list full_journey, integer forwards) +{ +//tell_owner("entry to warp across list, list size=" + (string)llGetListLength(full_journey)); + // make sure the list didn't run out. + if (llGetListLength(full_journey) == 0) return []; + if (forwards) { + // forwards traversal of the list. + vector next_jump = (vector)llList2String(full_journey, 0); + // shortcut the jumps if we're already there. + if (next_jump == llGetPos()) + return warp_across_list(chop_list(full_journey, 1, llGetListLength(full_journey) - 1), forwards); + // calculate our trajectory for the next jump and add in all subsequent jumps. + return process_warp_item(next_jump) + + warp_across_list(chop_list(full_journey, 1, llGetListLength(full_journey) - 1), forwards); + } else { + // reverse traversal of the list. + vector next_jump = (vector)llList2String(full_journey, llGetListLength(full_journey) - 1); + // shortcut the jumps if we're already there. + if (next_jump == llGetPos()) + return warp_across_list(chop_list(full_journey, 0, llGetListLength(full_journey) - 2), forwards); + // calculate our trajectory for the next jump and add in all subsequent jumps. + return process_warp_item(next_jump) + + warp_across_list(chop_list(full_journey, 0, llGetListLength(full_journey) - 2), forwards); + } +} + +// the main function that performs the jaunting process. +jaunt(list full_journey, integer forwards) +{ + // set up our global variables... + last_posn = llGetPos(); + // calculate the trip and run it. + llSetPrimitiveParams(warp_across_list(full_journey, forwards)); + // failsafe to patch up any math issues... + integer last_indy = 0; + if (forwards == TRUE) last_indy = llGetListLength(full_journey) - 1; + + // pick out the last target in the list based on the direction we're moving. + vector last_jump = (vector)llList2String(full_journey, last_indy); + integer max_attempts = 3; // a rough guess at most adjustments we'd ever need. + while ( (llVecDist(llGetPos(), last_jump) > .001) && (max_attempts-- > 0) ) { +////llWhisper(0, "touch up jump from " + (string)llGetPos()); + llSetPos(last_jump); + } +} + +//end borrowed from jaunt +////////////// + +// sends an object back along its trajectory. +reverse_velocity(integer local_axis) +{ + vector current_velocity = llGetVel(); + + if (local_axis) { + rotation rot = llGetRot(); + current_velocity /= rot; // undo the rotation. + } + + vector new_velocity = -2 * current_velocity; + new_velocity *= llGetMass(); + + llApplyImpulse(new_velocity, local_axis); +} + +////////////// + +// globals that record parameters during runtime. + +vector last_victim = <0.0, 0.0, 0.0>; + // the last place we collided or sensed something and decided to check out. + +vector previous_velocity; + // the rate the object was going when it started to hit something. + +// the steps taken when the bullet itself dies. +dying_scene() +{ + // make fire appear again. + llMakeExplosion(20, 1.0, 5, 3.0, 1.0, "fire", ZERO_VECTOR); + // replay the explosion noise, for good measure + llTriggerSound("Explosion", 1.0); + // make the smoke visible. it would technically be faster than the sound, but + // the call also pauses the script. + llMakeExplosion(20, 1.0, 5, 3.0, 1.0, "Smoke", ZERO_VECTOR); + // gone! + llDie(); +} + +fling_at(vector location) +{ + if (location == <0.0, 0.0, 0.0>) return; + vector proper_direction = location - llGetPos(); + set_velocity(100 * proper_direction, FALSE); +} + +jaunt_to_location(vector location) +{ + if (location == <0.0, 0.0, 0.0>) return; + // this turns off the physics property for the object, so that jaunt and + // llSetPos will still work. this became necessary due to havok4. + llSetStatus(STATUS_PHYSICS, FALSE); + llSetStatus(STATUS_PHANTOM, TRUE); + // go to position specified. + jaunt([llGetPos(), location], TRUE); + // return to prior characteristics. + llSetStatus(STATUS_PHANTOM, FALSE); + llSetStatus(STATUS_PHYSICS, TRUE); + +//simplistic version. +//// llSetPos(location); +} + +blow_up_item(key to_whack, integer type) +{ + if (to_whack == NULL_KEY) { + llOwnerSay("wtf? passed a null key in blow up item."); + return; + } + list details = llGetObjectDetails(to_whack, [ OBJECT_POS ]); + vector current_victim = llList2Vector(details, 0); +/// fling_at(current_victim); + jaunt_to_location(current_victim - <0.0, 0.0, 0.042>); + if (type & AGENT) { + llOwnerSay("targeting: " + llKey2Name(to_whack)); + // flame first. + llMakeExplosion(20, 1.0, 5, 3.0, 1.0, "fire", ZERO_VECTOR); + // reset the last victim for a *real* victim. + last_victim = current_victim; + } +//fling_at(last_victim);//trying new approach. + // then a huge push. + llPushObject(to_whack, push_magnitude * llRot2Up(llGetRot()), ZERO_VECTOR, FALSE); + // then the noise, if the victim is important enough. + if (type & AGENT) { + // make a glad noise. sound is slower, right? +llSetStatus(STATUS_PHYSICS, FALSE);//guess temp. + llTriggerSound("Explosion", 1.0); +llSetStatus(STATUS_PHYSICS, TRUE);//guess temp. + } +} + +reset_to_last_hit() +{ + jaunt_to_location(last_victim); // start back where we first collided. + // onward at nearly the same rate, but backwards. + vector adjusted = ; + set_velocity(-previous_velocity + adjusted, FALSE); + last_victim = <0.0, 0.0, 0.0>; // allow collisions to start being noticed again. + } + +integer blow_up_avatars(integer total_number) +{ +// fling_at(last_victim); + jaunt_to_location(last_victim); + +//if (last_victim != <0.0, 0.0, 0.0>) { +//full_stop(); +//// check where it actually thinks we hit. +//integer i; +//for (i = 0; i < 20; i++) +//llMakeExplosion(20, 1.0, 5, 3.0, 1.0, "fire", ZERO_VECTOR); +//} + + integer found_avatar = FALSE; + + // now whack the avatars. + integer i; + for (i = 0; i < total_number; i++) { + // if we hit an avatar, slam them away. + key curr_key = llDetectedKey(i); + if (curr_key != NULL_KEY) { + // why would the key ever be null? yet it seems we have seen that. + integer type = llDetectedType(i); + // make sure the target is an avatar and it's NOT the owner. + if (curr_key != llGetOwner()) { + // blow them up regardless of what type they are. + blow_up_item(curr_key, type); + // set that we found an avatar if the type is right. + if (type & AGENT) found_avatar = TRUE; + } + } + } + + if (!found_avatar) { + reset_to_last_hit(); + return FALSE; // don't die right now. we didn't achieve our objective. + } + + dying_scene(); + return TRUE; // i'm not sure we'll ever see this statement... +} + +integer enabled = FALSE; // true if bullet is ready to operate. + +integer hits_used = 0; // number of hits consumed so far on sensors. + +prepare_bullet() +{ +/// llSensorRemove(); + +// llSetDamage(5000); // hang onto that. +//what do they mean by "task will be killed"? + + // pick the object characteristics that seem to work best. + llSetStatus(STATUS_PHYSICS, TRUE); + llSetStatus(STATUS_PHANTOM, FALSE); + + hits_used = 0; + last_victim = <0.0, 0.0, 0.0>; +} + +limit_obnoxiousness() +{ + hits_used++; + if (hits_used > MAXIMUM_HITS) dying_scene(); +} + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default { + state_entry() { + auto_retire(); + llPreloadSound("Explosion"); + prepare_bullet(); + } + + on_rez(integer start_param) { + // make this a bit more specific so we can work on it. + if (start_param == 2814) { + prepare_bullet(); + enabled = TRUE; + } + } + + collision_start(integer total_number) + { + if (!enabled) return; + // initialize our boom position if it hasn't been yet. + if (last_victim == <0.0, 0.0, 0.0>) { + previous_velocity = llGetVel(); + last_victim = llGetPos(); +// reverse_velocity(FALSE); + // sense up any bogies. + llSensor("", NULL_KEY, AGENT | ACTIVE, sense_distance, 2.0 * PI); + } + } + + sensor(integer total_number) { + if (!enabled) return; + limit_obnoxiousness(); + // act on who we sensed around us. + integer found_one = blow_up_avatars(total_number); + // did we find an avatar? that makes us explode with pleasure. + if (!found_one) { + // start looking again. + llSensor("", NULL_KEY, AGENT | ACTIVE, sensory_addition + sense_distance, 2.0 * PI); + } else { + // we had seen one, so croak. + dying_scene(); + } + } + + no_sensor() { + if (!enabled) return; + limit_obnoxiousness(); + // try again until we find a victim. + reset_to_last_hit(); + llSensor("", NULL_KEY, AGENT | ACTIVE, 2.0 * sensory_addition + + sense_distance, 2.0 * PI); + } +} + diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/cowpole_ignite_v1.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/cowpole_ignite_v1.2.lsl new file mode 100755 index 00000000..f2f6688a --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/cowpole_ignite_v1.2.lsl @@ -0,0 +1,129 @@ + +// huffware script: cowpole ignite, by fred huffhines +// +// a simple bridge to get the lamp lit. this script needs to be in the lamp post and not in the bulb. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +integer SECRET_LIGHT_BULB_ILLUMINATI_CODE = -14058; + // a semi-secret code that is used in a linked message when some other script + // wants the lamp to turn on or off. + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { auto_retire(); } + touch_start(integer total_number) + { + llMessageLinked(LINK_SET, SECRET_LIGHT_BULB_ILLUMINATI_CODE, "", ""); + } +} + diff --git a/huffware/huffotronic_updater_freebie_v5.3/create_objects_v1.3.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/create_objects_v1.3.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/create_objects_v1.3.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/create_objects_v1.3.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/data_cow_v3.3.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/data_cow_v3.3.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/data_cow_v3.3.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/data_cow_v3.3.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/docs_for_searchbert_v2.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/docs_for_searchbert_v2.2.lsl new file mode 100755 index 00000000..1a4e1a30 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/docs_for_searchbert_v2.2.lsl @@ -0,0 +1,76 @@ +=================================== +info about the searchbert seeker robot +=================================== + +### +version 46.4 +released February 26 2012 +### + +author=fred huffhines (osgrid and second life avatar) +source=eepaw shop (see below) +price=*free* +** note: if you paid for this, someone ripped you off. please let fred huffhines in osgrid or second life know about this. +license = gpl [ http://www.gnu.org/licenses/gpl.html ] within metaverse (osgrid, opensims, second life, etc). + + searchbert is a full-perm second life search engine that i created for the free scripts library (which used to be in the Solvo region of second life, but which no longer exists, sadly). + + this device can find matches for nearby objects given just a part of the object's name. after doings its scan, searchbert points out all the objects with multi-colored particle beams. + + the objects can be scattered at different heights and distances, even fairly far away from searchbert, and still be accurately located. avatars will also be found if their names match the search. searchbert can point out up to 17 objects and avatars. + + if you 'Touch' searchbert, then it will tell you its usage info. here is a more detailed version of the help information. + you can speak these commands near searchbert in open chat to give it some orders: + + #find X + looks for any object nearby with "X" in its name. + try this one out: "#find primitive" (in osgrid) or "#find object" (in second life) + that will find any items that have been left with their respective default names. + and this is a nice one: "#find e" + finds any object whose name contains the letter 'e'. that's usually very many things. + + #reset + clears any existing search matches that had been found. the matches will eventually time-out and be forgotten anyhow, but this makes searchbert forget the search immediately. + if this command is used by the owner of searchbert while a search is active, it stops the search immediately, but doesn't clear any matches that may already have been found. #reset can only be used this way by the owner, which avoids random people stopping each other's searches. + + #initialize + hard core brain-clearing command for the searchbert; makes it fully clear out and restart its scripts. + + #skew + *bonus* a debugging command that makes searchbert point its arms in all directions. + + #channel + allows the listening channel for searchbert to be changed. example: + #channel 42 + this makes searchbert listen for any commands on channel 42, a fact which should be reflected + in the menus and chat after this.. + +that's about it for the basics... enjoy! +--fred huffhines + +==== +more detailed and nutty searchbert notes... + +* searchbert builds upon original scripts by christopher omega and ama omega, which gave me a starting point for the particle system and link message communication. i have put quite a lot of time into the searchbert also, to make it zoom around in a spiral and so forth. + +* searchbert gets around second life's limitations on the number of search matches by moving around and looking at things from different angles. it uses a spiral search pattern to traverse the area. it has multiple "seeker rods" to find matches, and these are aimed in different directions to increase the likelihood of finding matches. further, during the search, searchbert is gradually rotated to different orientations to cover even more area. + +* you can rip all of searchbert's arms off and it won't feel a thing! rez a searchbert on land where you have permissions, right-click on it and select "Edit". when the editing dialog has popped up, hit this key combination: "ctrl-shift-L". that is the "remove all links" shortcut command, and it will separate all the arms from searchbert's main body (the sphere). the arms will take this very poorly and all die, but the sphere will survive. after that ugliness, clicking on searchbert will make it perform its "putting my arms back on" schtick for about 17 seconds. it needs permission to attach objects, and will ask for it. after the arms are finished being snapped back on, searchbert will be fully functional again. + +* release 46.0 sees the arm reattachment code in a separate script. we were doing fine with version 42.0 in open simulator, but second life had other ideas about how much space a script is allowed to occupy when compiled. this forced us to separate out the code for the arm reattachment procedures in a 'searchbert armature' script. this script also takes care of synchronizing content with the seeker arms. fracturing searchbert's code in this way actually led to a few simplifications, so maybe it's now better overall, but being _forced_ to refactor is not exactly the ideal process for optimal software development. + +* as of searchbert 42.0, there is a notecard configuration system and a menuing system. the options available in the notecards for configuration are broader than what is available in the menu interface, so far. +due to how scripts work, if you reset the scripts, then any items configured from the menu are forgotten and searchbert will fall back to whatever is specified in the notecard. + +* the search range at which objects will be found can be re-configured inside searchbert's "searchbert config vX.Y" notecard. there are two parts to this. the max_spiral_radius variable is the maximum distance searchbert will travel away from its starting point. the sensor_max_range variable is the maximum distance used for a sensor scan. so the maximum distance from its starting point that searchbert will ever be able to find an object at is max_spiral_radius + sensor_max_range. + +* as of searchbert 32.5, searchbert is low lag: + old searchbert 31.3 crushed performance of densely packed and also second life's "openspace sims" before, eating up to 10% of the sim's recommended cpu. + that was then, version 32.5 is now (or was "now" when i wrote this screed); it uses about 10% of its former resources (so more like 1% or less of sim cpu) when it's active. thanks very much to eren padar for measuring its performance with elf clan's estate tools. + also this version doesn't ask for link permissions unless it needs to put its arms back on. thanks to hippyjim starbrook for that suggestion. + +* searchbert is the freebie mascot for our store, Eclectic Electric Patterns and Widgets (eepaw). + check out the licensing notecard for our store locations. + +* thanks for reading the extended notes. you are a patient, patient person. --fred huffhines. + diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/eepaw_shop_note_v0.8.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/eepaw_shop_note_v0.8.lsl new file mode 100755 index 00000000..d637d22d --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/eepaw_shop_note_v0.8.lsl @@ -0,0 +1,20 @@ +Dateline: 2011 07 02 +Location: eepaw shop [Eclectic Electric Patterns And Widgets] + +Greetings Fellow Slackers, + +Let it be known that the eepaw freebie store location is in the osgrid in the Wright Plaza region: + osgrid.org -- Wright Plaza <228, 22, 21> + (there is lots of info about osgrid at: http://osgrid.org/ and you can get a free account here: http://www.osgrid.org/index.php/auth/register). + +And our old shop in Second Life is still hanging in there in the Smoky region: + http://slurl.com/secondlife/Smoky/14/84/404 + +Plus we have a twitter feed now provided by a helpful hamster: + http://twitter.com/#!/fred_hamster + +Please do come and visit the eepaw shop for fun times and to see our weird gadgets and widgets, and furthur for surreal and psychedelic textures. We are awaiting your arrival with hot beverages and a pleasant attitude. The beverages are virtual, but the attitude is real. + +Thanks Folks, +Fred Huffhines, +eepaw shop proprietor. diff --git a/huffware/huffotronic_updater_freebie_v5.3/fade_opacity_v3.7.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/fade_opacity_v3.7.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/fade_opacity_v3.7.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/fade_opacity_v3.7.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/fade_prim_v3.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/fade_prim_v3.2.lsl new file mode 100755 index 00000000..95e89026 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/fade_prim_v3.2.lsl @@ -0,0 +1,158 @@ + +// huffware script: fade prim, by fred huffhines +// +// makes an object fade in and out of visibility. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// constants... + +float OPACITY_MIN = 0.4; // the lowest value to allow the transparency to reach. +float OPACITY_MAX = 1.0; // the highest value to allow the transparent + // if you set this above the max of 1.0, it keeps the object solid for longer. + +float TIMER_INTERVAL = 1.5; // how often do we adjust our appearance? + +// variables... + +float current_opacity = 0.42; // what is our current opacity? +float current_adjustment = 0.2; // amount added/removed each time. + +////////////// +// from hufflets... + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on an unusual channel for chat if it's not intended for general public. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat that anyone can hear. we take off the bling for this one. +// llSay(0, to_say); +} +// +////////////// + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.4. +// distributed under BSD-like license. +// partly based on the self-upgrading scripts from markov brodsky and jippen faddoul. +// the function auto_retire() should be added *inside* a version numbered script that +// you wish to give the capability of self-upgrading. +// this script supports a notation for versions embedded in script names where a 'v' +// is followed by a number in the form "major.minor", e.g. "grunkle script by ted v8.2". +// when the containing script is dropped into an object with a different version, the +// most recent version eats any existing ones. +// keep in mind that this code must be *copied* into your script you wish to add +// auto-retirement capability to. +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + llSetTimerEvent(TIMER_INTERVAL); + } + + timer() { + llSetTimerEvent(0); + llSetLinkAlpha(LINK_SET, current_opacity, ALL_SIDES); + current_opacity += current_adjustment; + if (current_opacity < OPACITY_MIN) { current_opacity = OPACITY_MIN; current_adjustment *= -1.0; } + if (current_opacity > OPACITY_MAX) { current_opacity = OPACITY_MAX; current_adjustment *= -1.0; } + llSetTimerEvent(TIMER_INTERVAL); + } +} + diff --git a/huffware/huffotronic_updater_freebie_v5.3/fredboxmux_no_rot_v3.0.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/fredboxmux_no_rot_v3.0.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/fredboxmux_no_rot_v3.0.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/fredboxmux_no_rot_v3.0.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/fredboxmux_v3.0.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/fredboxmux_v3.0.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/fredboxmux_v3.0.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/fredboxmux_v3.0.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/giftorse_v7.6.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/giftorse_v7.6.lsl new file mode 100755 index 00000000..047c7121 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/giftorse_v7.6.lsl @@ -0,0 +1,451 @@ + +// huffware script: giftorse, by fred huffhines +// +// this script advertises a product in a store. +// how to configure this script: the object name is known automatically, but the seller +// and store names are not. also the cost is not currently automatic, since there seems +// to be no way to figure it out. all of the variable data can be stored in a notecard +// in the following format: +// +// #giftorse +// cost = N +// creator = X +// shop = S +// label = L +// +// where N is a numerical cost for the item in linden dollars, X is the name of the creator +// of the object, S is the name of the shop where the object is being sold, and L is a label +// to show above the object. +// +// this script supports a sales paradigm, where normally it just shows the +// appropriate title, but once the customer has bought it, it will permanently set +// it's last title and exit, but not before telling the user how to get the contents. +// this script depends on the "non-script giver" script also being in the same object. +// that's what let's it promise that the stuff will be given out on touch. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + + +// every product has a different cost. +string DEFAULT_COST = "**UNSET**"; +string OBJ_COST; + +// these are hard-coded for my own shop, since it's more convenient for me. +// remember to change these to your name and store name. +string SCRIPT_CREATOR = "Fred Huffhines"; // uber-default, do not change. +string CREATOR = "Fred Huffhines"; // change this if you are not me. +string SHOP_NAME = "eepaw shop (Eclectic Electric Patterns and Widgets)"; + +// if this is anything other than "default", then the text is used as the label. +// if left as "default", then the object's own name is used as the label. +string OBJECT_LABEL = "default"; + +// the color of the text above the object. +vector TEXT_COLOR = <0.6, 0.7, 0.8>; + +// how long should the text stay on the object after a potential customer clicks it? +float DELAY_BEFORE_CLEARING_TEXT = 10.0; + // a fairly liberal allowance for how long it might take to read the object. + // we are trying to balance information flow with how obnoxious people feel text labels are. + +////////////// + +// constants that should really stay constant, like they are now... + +string GIFTORSE_SIGNATURE = "#giftorse"; // the expected first line of our notecards. + +////////////// + +// global variables... + +string global_notecard_name; // name of our notecard in the object's inventory. + +integer response_code; // set to uniquely identify the notecard read in progress. + +// programmer friendly variables... + +integer DEBUGGING = FALSE; + // if true, then the code will be noisy during its processing. + +// requires noteworthy library v3.3 or higher. +////////////// +// do not redefine these constants. +integer NOTEWORTHY_HUFFWARE_ID = 10010; + // the unique id within the huffware system for the noteworthy script to + // accept commands on. this is used in llMessageLinked as the num parameter. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +string BAD_NOTECARD_INDICATOR = "bad_notecard"; + // indicates that the notecard reading process has failed to find an appropriate one. +string NOTECARD_READ_CONTINUATION = "continue!"; + // returned as first parameter if there is still more data to handle. +////////////// +// commands available via the noteworthy library: +string READ_NOTECARD_COMMAND = "#read_note#"; + // command used to tell the script to read notecards. needs a signature to find + // in the card as the only parameter. the signature can be empty or missing. + // the results will be fired back as the string value returned, which will have + // an embedded list that was read from the notecard. this necessarily limits the + // size of the notecards that we can read and return. +// +// joins a list of parameters using the parameter sentinel for the library. +string wrap_parameters(list to_flatten) +{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); } +////////////// + +////////////// + +// from hufflets... + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat, but use an unusual channel. +// llSay(108, (string)debug_num + "- " + to_say); +} + +///////////// + +// returns a number at most "maximum" and at least "minimum". +// if "allow_negative" is TRUE, then the return may be positive or negative. +float randomize_within_range(float minimum, float maximum, integer allow_negative) +{ + if (minimum > maximum) { + // flip the two if they are reversed. + float temp = minimum; minimum = maximum; maximum = temp; + } + float to_return = minimum + llFrand(maximum - minimum); + if (allow_negative) { + if (llFrand(1.0) < 0.5) to_return *= -1.0; + } + return to_return; +} + +// returns a non-empty string if "to_check" defines a value for "variable_name". +// this must be in the form "X=Y", where X is the variable_name and Y is the value. +string get_variable_value(string to_check, string variable_name) +{ + // clean initial spaces. + while (llGetSubString(to_check, 0, 0) == " ") + to_check = llDeleteSubString(to_check, 0, 0); + if (!is_prefix(to_check, variable_name)) return ""; + to_check = llDeleteSubString(to_check, 0, llStringLength(variable_name) - 1); + // clean any spaces or valid assignment characters. + while ( (llGetSubString(to_check, 0, 0) == " ") + || (llGetSubString(to_check, 0, 0) == "=") + || (llGetSubString(to_check, 0, 0) == ",") ) + to_check = llDeleteSubString(to_check, 0, 0); + if (DEBUGGING) + log_it("set " + variable_name + " = " + to_check); + string chewed_content = to_check; + integer indy; + for (indy = 0; indy < llStringLength(chewed_content); indy++) { + if (llGetSubString(chewed_content, indy, indy) == "\\") { + if (llGetSubString(chewed_content, indy+1, indy+1) == "n") { + chewed_content = llGetSubString(chewed_content, 0, indy - 1) + + "\n" + + llGetSubString(chewed_content, indy + 2, + llStringLength(chewed_content) - 1); + } + } + } + // return what's left of the string. + return chewed_content; +} + +///////////// + +// is the merchant/shop guy still the owner? +integer merchant_owns() +{ + string owner_now = llToLower(llKey2Name(llGetOwner())); + if ( (owner_now != "") && (owner_now != llToLower(CREATOR)) + && (owner_now != llToLower(SCRIPT_CREATOR) ) ) { + // we're not able to compare the owner to what we expect. + return FALSE; + } + return TRUE; +} + +// returns the index of the first occurrence of "pattern" inside +// the "full_string". if it is not found, then a negative number is returned. +integer find_substring(string full_string, string pattern) +{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); } + +// applies a text label above the object. +set_text(integer enabled) +{ + if (enabled) { + string object_label = OBJECT_LABEL; + // reset the label to a decorated version of object name if it was default. + if (object_label == "default") { + list name_bits = compute_basename_and_version(llGetObjectName()); + string name = llList2String(name_bits, 0); + if (name == "") name = llGetObjectName(); // didn't split, use the full name. + object_label = "[ " + name + " ]"; + } +//hmmm: retired, since it's an untruth to people nearby who don't own it. +// if (!merchant_owns()) object_label += "\n-\nClick on this object to get its contents."; + llSetText(object_label, TEXT_COLOR, 1.0); + } else { + // no label, no how. + llSetText("", TEXT_COLOR, 0.0); + } +} + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return (llSubStringIndex(compare_with, prefix) == 0); } + +parse_variable_definition(string to_parse) +{ + string content; // filled after finding a variable name. + if ( (content = get_variable_value(to_parse, "cost")) != "") + OBJ_COST = content; + else if ( (content = get_variable_value(to_parse, "creator")) != "") + CREATOR = content; + else if ( (content = get_variable_value(to_parse, "shop")) != "") + SHOP_NAME = content; + else if ( (content = get_variable_value(to_parse, "label")) != "") + OBJECT_LABEL = content; + else if ( (content = get_variable_value(to_parse, "text_color")) != "") + TEXT_COLOR = (vector)content; +} + +// handles a set of information from the notecard about this product. +process_product_definition(list lines) +{ + integer indy; + for (indy = 0; indy < llGetListLength(lines); indy++) { + string line = llList2String(lines, indy); + // try to interpret that as a variable setting. + parse_variable_definition(line); + } +} + +// display information about the product in open chat. +show_product_info() +{ + string product_name = llGetObjectName(); + string cost_string = "L$" + OBJ_COST + "."; + if (OBJ_COST == "0") cost_string = "free!"; + llSay(0, " is available for " + cost_string + "\n" + + "[Created by " + CREATOR + ", Sold by " + SHOP_NAME + "]"); +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.4. +// distributed under BSD-like license. +// partly based on the self-upgrading scripts from markov brodsky and jippen faddoul. +// the function auto_retire() should be added *inside* a version numbered script that +// you wish to give the capability of self-upgrading. +// this script supports a notation for versions embedded in script names where a 'v' +// is followed by a number in the form "major.minor", e.g. "grunkle script by ted v8.2". +// when the containing script is dropped into an object with a different version, the +// most recent version eats any existing ones. +// keep in mind that this code must be *copied* into your script you wish to add +// auto-retirement capability to. +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + set_text(FALSE); // no label to start out. + global_notecard_name = ""; + response_code = 0; + OBJ_COST = DEFAULT_COST; // reset in opensim friendly way. + + // see if we can load a notecard for product info. + response_code = -1 * (integer)randomize_within_range(23, 80000, FALSE); + string parms_sent = wrap_parameters([GIFTORSE_SIGNATURE, response_code]); + llMessageLinked(LINK_THIS, NOTEWORTHY_HUFFWARE_ID, READ_NOTECARD_COMMAND, + parms_sent); + llSetTimerEvent(32); // make sure that if we don't get a config, we try again. + } + + // processes link messages received from support libraries. + link_message(integer which, integer num, string msg, key id) { + if (num != NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE) return; // not for us. + if (msg == READ_NOTECARD_COMMAND) { + // process the result of reading the notecard. + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + string notecard_name = llList2String(parms, 0); + integer response_for = llList2Integer(parms, 1); + if (response_for != response_code) { + // oops, this isn't for us. stop looking at it. + return; + } + + integer done_reading = TRUE; + if (notecard_name == NOTECARD_READ_CONTINUATION) done_reading = FALSE; + if (notecard_name != BAD_NOTECARD_INDICATOR) { + global_notecard_name = notecard_name; + list lines = llDeleteSubList(parms, 0, 1); + process_product_definition(lines); + } else { + // we hated the notecards we found, or there were none. +// log_it("There is no product definition found. We will proceed with defaults."); +// state describe_product_and_neigh; + } + if (done_reading) { + // since we got our final response for config, stop the timer. + llSetTimerEvent(0); + state describe_product_and_neigh; + } + } + } + + on_rez(integer parm) { llResetScript(); } + + timer() { + log_it("failed to read configuration; retrying."); + llResetScript(); + } + +} + +// this is the active state, once we have read our product configuration from +// a notecard. +state describe_product_and_neigh +{ + state_entry() { + set_text(FALSE); // clear our text label just in case. + if (OBJ_COST == DEFAULT_COST) { + log_it("resetting due to missing configuration."); + llResetScript(); + } + show_product_info(); + if (!merchant_owns()) { +//hmmm: potentially might want to allow for multiple merchants so your partners don't go nuts hearing this. + // rip this script out, since it's not intended for other folks. + llSay(0, "\n\nThanks for your purchase from " + SHOP_NAME + + "!\nTo get your product's contents, just click this package."); + } + } + + on_rez(integer parm) { llResetScript(); } + + touch_start(integer total_number) { + set_text(TRUE); // let them see our name. + show_product_info(); + llSetTimerEvent(DELAY_BEFORE_CLEARING_TEXT); + } + + timer() { + // the only reason we should be here is if we are trying to clear up our text. + set_text(FALSE); + llSetTimerEvent(0); + } + + changed(integer change) { + if (change & CHANGED_INVENTORY) { + llSleep(3.14159265358); + // snooze to ensure that we don't reset before a new version gets a + // chance to remove this. + llResetScript(); // start over. + } + } +} + +//eternal questions... + +//hmmm: why can't we automatically get the object's sale price? diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/huff-pet_v18.6.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/huff-pet_v18.6.lsl new file mode 100755 index 00000000..1397e17c --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/huff-pet_v18.6.lsl @@ -0,0 +1,963 @@ + +// huffware script: huff-pet, by fred huffhines +// +// this is yet another implementation of a pet script in LSL. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +//to-do zone: + +//hmmm: for attack mode, adjust timers and ranges. +// probably just define the default set and mirror that with the attack set. +// switch between them during mode change. +// reset timer and sensor for mode change! + +// end to-do zone. + +// constants for the pet that one might want to change. + +integer PET_CHAT_CHANNEL = 28; + // the channel on which the pet will listen for commands from the owner. + +//integer DEFAULT_PANIC_DISTANCE = 28; +integer DEFAULT_PANIC_DISTANCE = 5;//short leash version for opensim. + // multiplied by the sensor range to get the distance allowed from the + // pet to the person it's following before a teleport is invoked. +integer ATTACK_PANIC_DISTANCE = 13; + // multiplied by the sensor range to get the distance allowed from the + // enraged pet to the attack target before a teleport is invoked. + +float DEFAULT_HEIGHT_ABOVE_FOLLOWED_OBJECT = 1.6; + // the height that the pet will float at above whoever it's following. +float ATTACK_HEIGHT_ABOVE_FOLLOWED_OBJECT = 0.0; + // the height that the pet will float at above the attack target. + +float DEFAULT_BASE_VELOCITY = 0.1; + // the velocity of the pet when just cavorting around. +float ATTACK_BASE_VELOCITY = 0.6; + // the velocity of the pet when in "angry" mode. + +integer DEFAULT_BUMP_SIZE = 48; + // the default size in meters of the displacement caused by up, down, etc commands. + +// the margin values below are the range of motion that the pet is allowed +// on each axis. +float DEFAULT_X_MARGIN = 3.0; +float DEFAULT_Y_MARGIN = 3.0; +float DEFAULT_Z_MARGIN = 1.0; +// the margin for when the pet is attacking. +float ATTACK_X_MARGIN = 0.1; +float ATTACK_Y_MARGIN = 0.1; +float ATTACK_Z_MARGIN = 0.2; + +float MAXIMUM_TARGETING_DISTANCE = 2.0; + // the amount of basic deviation allowed for the pet from its target spot. + // this is how far it's allowed to roam from the target. +//is that right? + +float ATTACK_PUSH_DIST_THRESHOLD = 2.0; + // how close the pet should be to an attack target before trying to push. +float ATTACK_PUSH_MAGNITUDE = 2147483646.0; //maxint - 1, dealing with svc-2723. + // how much the critter should push an attack target. +float ATTACK_PUSH_CHANCE = 0.1; + // how often (probability from 0.0 to 1.0) an attack target gets pushed. + +// other constants that are more advanced and should generally not change... + +float TARGETING_SENSOR_RANGE = 96.0; + // the maximum distance the pet will try to see the target at. + +float SENSOR_INTERVAL = 0.4; + // how often the sensor scan will fire off. this is the fastest we will + // check for our follow target, in seconds. + +float PERIODIC_INTERVAL = 0.42; + // how frequently our timer event fires. + +integer MAXIMUM_SLACKNESS = 28; + // how many timer hits we'll allow before reverting to the default state. + +float VELOCITY_MULTIPLIER = 1.2; + // the total velocity comes from the base plus an equation that multiplies + // this value by some function of the distance. + +string PET_MENU_NAME = "#woof"; // name for our menu. +string PET_REPLY_MENU = "#aroo"; // replies with data. + +// symbolic labels for the different states of pet 'being'. +integer STATE_STAY = 0; +integer STATE_FREE = 1; // go home. +integer STATE_FOLLOW = 2; +integer STATE_COME = 3; +integer STATE_WANDER = 4; +integer STATE_ATTACK = 9; + +list SUPPORTED_COMMANDS = [ "go home", "come", "stay", + "wander", "follow", "attack", + "set home", "set name", "status" ]; + // attack = follow an avatar in a menacing way. + // come = follow owner. + // follow = follow an avatar. + // go home = return home, then move about freely. + // set home = set the home position based on owner location. + // set name = change the object's name. + // stay = sit right there. + // wander = roam a greater distance while following owner. + +// requires: jaunting library v3.4 or higher. +////////////// +// do not redefine these constants. +integer JAUNT_HUFFWARE_ID = 10008; + // the unique id within the huffware system for the jaunt script to + // accept commands on. this is used in llMessageLinked as the num parameter. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +////////////// +// commands available via the jaunting library: +string JAUNT_COMMAND = "#jaunt#"; + // command used to tell jaunt script to move object. pass a vector with the location. +string FULL_STOP_COMMAND = "#fullstop#"; + // command used to bring object to a halt. +string REVERSE_VELOCITY_COMMAND = "#reverse#"; + // makes the object reverse its velocity and travel back from whence it came. +string SET_VELOCITY_COMMAND = "#setvelocity#"; + // makes the velocity equal to the vector passed as the first parameter. +string JAUNT_UP_COMMAND = "#jauntup#"; +string JAUNT_DOWN_COMMAND = "#jauntdown#"; + // commands for height adjustment. pass a float for number of meters to move. +string JAUNT_LIST_COMMAND = "#jauntlist#"; + // like regular jaunt, but expects a list of vectors as the first parameter; this list + // should be in the jaunter notecard format (separated by pipe characters). + // the second parameter, if any, should be 1 for forwards traversal and 0 for backwards. +// +////////////// + +//requires menutini library v4.2 or better. +////////////// +// do not redefine these constants. +integer MENUTINI_HUFFWARE_ID = 10009; + // the unique id within the huffware system for the jaunt script to + // accept commands on. this is used in llMessageLinked as the num parameter. +// commands available via the menu system: +string SHOW_MENU_COMMAND = "#menu#"; + // the command that tells menutini to show a menu defined by parameters + // that are passed along. these must be: the menu name, the menu's title + // (which is really the info to show as content in the main box of the menu), + // the wrapped list of commands to show as menu buttons, the menu system + // channel's for listening, and the key to listen to. + // the reply will include: the menu name, the choice made and the key for + // the avatar. +// +////////////// + + +request_full_stop() +{ + llMessageLinked(LINK_THIS, JAUNT_HUFFWARE_ID, FULL_STOP_COMMAND, ""); +} + +request_jaunt_up(integer distance) +{ + llMessageLinked(LINK_THIS, JAUNT_HUFFWARE_ID, JAUNT_UP_COMMAND, (string)distance); +} + +// global variables... + +key _OWNER; +integer current_state; +vector home_position; // the location where the pet lives. + +integer pending_target = FALSE; // is a target still being sought. + +integer _COMMAND_CHANNEL; +string _COMMAND_MESSAGE = "How may I assist you?"; + +integer _TARGET_ID; +vector TARGET_POSITION; + +float _FREE_RANGE = 10.0; + +string SIT_TEXT = "Meditate"; + +string SIT_ANIMATION = "yoga_float"; + +vector SIT_POSITION = <0.2, 0.2, 0.4>; + +vector SIT_ROTATION = <0.0, 0.0, 0.0>; + +key SITTING_AVATAR_KEY = NULL_KEY; + +// options for follow menu that pops up when pet is told to follow someone. +list _FOLLOW_KEY; +list _FOLLOW_NAME; // filled in with nearby avatars. +integer _FOLLOW_CHANNEL; +string _FOLLOW_MESSAGE = "Who should I follow?"; + +integer seeking_avatars = FALSE; +key KEY_OF_TARGET; + +////////////// + +// from hufflets... + +// locates the string "text" in the list to "search_in". +integer find_in_list(list search_in, string text) +{ + integer len = llGetListLength(search_in); + integer i; + for (i = 0; i < len; i++) { + if (llList2String(search_in, i) == text) + return i; + } + return -1; +} + +/////////////// + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay((string)debug_num + "- " + to_say); + // say this on open chat, but use an unusual channel. +// llSay(108, (string)debug_num + "- " + to_say); +} + +/////////////// + +// info... + +// returns a string version of the state 's'. +string name_for_state(integer s) { + if (s == STATE_STAY) return "stay"; + if (s == STATE_FREE) return "go home"; + if (s == STATE_FOLLOW) return "follow"; + if (s == STATE_COME) return "come"; + if (s == STATE_WANDER) return "wander"; + if (s == STATE_ATTACK) return "attack"; + return "unknown"; +} + +// menu methods... + +list current_buttons; // holds onto the set of menu options. + +integer random_channel() { return -(integer)(llFrand(40000) + 20000); } + +string stringize_list(list to_flatten) { + return llDumpList2String(to_flatten, HUFFWARE_ITEM_SEPARATOR); +} + +// pops up a menu to interact with the pet's owner. +show_menu(string menu_name, string title, list buttons, integer channel) +{ + current_buttons = buttons; + key listen_to = _OWNER; + llMessageLinked(LINK_THIS, MENUTINI_HUFFWARE_ID, SHOW_MENU_COMMAND, + menu_name + HUFFWARE_PARM_SEPARATOR + + title + HUFFWARE_PARM_SEPARATOR + stringize_list(current_buttons) + + HUFFWARE_PARM_SEPARATOR + (string)channel + + HUFFWARE_PARM_SEPARATOR + (string)listen_to); +} + +// causes a state change to make the pet stay. +enter_stay_state() +{ + current_state = STATE_STAY; + llSensorRemove(); + stop_pet(); +} + +// handle the response message when the user chooses a button. +react_to_menu(integer sender, integer num, string msg, key id) +{ +log_it("react menu: " + msg + " parm=" + (string)id); + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + string menu_name = llList2String(parms, 0); + string choice = llList2String(parms, 1); + + if ( (num != MENUTINI_HUFFWARE_ID + REPLY_DISTANCE) || (msg != SHOW_MENU_COMMAND) ) { +log_it("why here in react to menu, not for us?"); + return; + } + + if (menu_name == PET_MENU_NAME) { +log_it("react: snd=" + (string)sender + " num=" + (string)num + " msg=" + msg + " key=" + (string)id); + if (find_in_list(SUPPORTED_COMMANDS, choice) < 0) { + llOwnerSay("i don't know what you mean..."); + return; + } +llOwnerSay("i heard you tell me: menu=" + menu_name + " choice=" + choice); + + // handle commands that are major changes in state... + + if (choice == "come") { + llOwnerSay("coming to find you now..."); + current_state = STATE_COME; + llSetPrimitiveParams([PRIM_PHYSICS, TRUE, + //]); + PRIM_PHANTOM, TRUE]); + llSensorRemove(); + llSensorRepeat("", _OWNER, AGENT, TARGETING_SENSOR_RANGE, PI, SENSOR_INTERVAL); + } + + if (choice == "stay") { + llOwnerSay("i will stay right here..."); + enter_stay_state(); + } + + if (choice == "wander") { + current_state = STATE_WANDER; + llOwnerSay("i'm going to wander around and kind of vaguely follow you..."); + llSetPrimitiveParams([PRIM_PHYSICS, TRUE, + //]); + PRIM_PHANTOM, TRUE]); + llSensorRemove(); + llSensorRepeat("", _OWNER, AGENT, TARGETING_SENSOR_RANGE, PI, SENSOR_INTERVAL); + } + + if ( (choice == "follow") || (choice == "attack") ) { + seeking_avatars = TRUE; + stop_pet(); + llSensorRemove(); + if (choice == "attack") { + // we only attack avatars. + //or not. since that's boring. watching a pet attack a physical object is fun. + llSensor("", NULL_KEY, AGENT | ACTIVE, TARGETING_SENSOR_RANGE, PI); + current_state = STATE_ATTACK; + } else { + // look for both objects and avatars to follow. + llSensor("", NULL_KEY, AGENT | ACTIVE, TARGETING_SENSOR_RANGE, PI); + current_state = STATE_FOLLOW; + } + } + + if (choice == "go home") { + current_state = STATE_FREE; // free to roam about the cabin, or wherever home is. + llOwnerSay("i'm going home now."); + jaunt_to_location(home_position); + } + + // commands that don't lead to state changes... + + if (choice == "status") { + string seek_addition; + if (KEY_OF_TARGET != "") + seek_addition = "was last seeking " + llKey2Name(KEY_OF_TARGET); + llOwnerSay("my name is " + llGetObjectName() + " and state is '" + + name_for_state(current_state) + "'.\n" + + seek_addition); + } + + if (choice == "set home") { + list pos_list = llGetObjectDetails(llGetOwner(), [OBJECT_POS]); + home_position = llList2Vector(pos_list, 0); + llOwnerSay("i'm setting my home to " + (string)home_position); + //hmmm: use a rounding print to show the position. + } + if (choice == "set name") { + llOwnerSay("to change my name from " + llGetObjectName() + ",\ntell me my new name by typing:\n/" + (string)PET_CHAT_CHANNEL + " name My Cool New Name"); + } + + } else if (menu_name == PET_REPLY_MENU) { +log_it("menu-act follow: snd=" + (string)sender + " num=" + (string)num + " msg=" + msg + " id=" + (string)id); + llSetPrimitiveParams([PRIM_PHYSICS, TRUE, + //]); + PRIM_PHANTOM, !(current_state == STATE_ATTACK)]); + integer choice_indy = find_in_list(_FOLLOW_NAME, choice); + if (choice_indy < 0) { +//log_it("choice was not found in list"); +//log_it("followname list is: " + (string)_FOLLOW_NAME); + } else { + string action = "follow"; + if (current_state == STATE_ATTACK) action = "attack"; + llOwnerSay("now " + action + "ing " + + llList2String(_FOLLOW_NAME, choice_indy) + "..."); + seeking_avatars = FALSE; + KEY_OF_TARGET = llList2Key(_FOLLOW_KEY, choice_indy); + llSensorRemove(); + llSensorRepeat("", KEY_OF_TARGET, AGENT | ACTIVE, + TARGETING_SENSOR_RANGE, PI, SENSOR_INTERVAL); + } + } +} + +// processes the hits that we get back from the sensor. the information we receive +// is needed for most of the pet states. +handle_sensor(integer num_detected) +{ + if (current_state == STATE_COME) { + go_to_target(_OWNER, llDetectedPos(0)); + motivate(); + } + + if ( (current_state == STATE_FOLLOW) || (current_state == STATE_ATTACK) ) { + if (seeking_avatars) { + // reset the list of keys and names that were found previously. + _FOLLOW_KEY = []; + _FOLLOW_NAME = []; + // show the full set found if it will fit, otherwise just 12. + integer num_to_show = num_detected; + if (num_to_show > 12) num_to_show = 12; + // examine each of the avatars found and put them on the list. + integer i; + for (i = 0 ; i < num_to_show; i++) { + key to_follow = llDetectedKey(i); + if (to_follow != NULL_KEY) { + _FOLLOW_KEY += [to_follow]; + string str = llDetectedName(i); + // trim the menu item if it has hit the maximum limit. + if (llStringLength(str) > 24) str = llGetSubString(str, 0, 23); + integer name_try = 0; + while (find_in_list(_FOLLOW_NAME, str) >= 0) { + // this guy is already listed under that name, so change it a bit. + str = llGetSubString(str, 0, 22) + (string)name_try++; + } + _FOLLOW_NAME += [str]; + } + } + // now ask who to follow. + if (llGetListLength(_FOLLOW_KEY)) { + show_menu(PET_REPLY_MENU, _FOLLOW_MESSAGE, _FOLLOW_NAME, _FOLLOW_CHANNEL); + } + } else { + // not seeking the avatar any more; follow who was chosen. + go_to_target(KEY_OF_TARGET, llDetectedPos(0)); + motivate(); + } + } + + if (current_state == STATE_WANDER) { + if (jaunt_responses_awaited) return; // skip doing anything while we're still waiting. + vector pos = llDetectedPos(0); + float omg = llFrand(1) * PI * 2; + float t_r = llFrand(1) * _FREE_RANGE; + float t_x = t_r * llCos(omg); + float t_y = t_r * llSin(omg); + go_to_target(NULL_KEY, pos + ); + motivate(); + } + +} + +handle_timer() { + if (current_state != STATE_STAY) { + // make sure a bad jaunt didn't break our physics. + llSetStatus(STATUS_PHYSICS, TRUE); + } + + if (jaunt_responses_awaited) { + // we are not quite there yet. + if (slackness_counter++ > MAXIMUM_SLACKNESS) { + // go back to the main state. we took too long. +log_it("waiting for jaunt timed out."); +///argh? jaunt_responses_awaited--; + slackness_counter = 0; + } else return; // not time yet for rest of timed actions. + } + + // handle the free state, since we need may to readjust the target. + if (current_state == STATE_FREE) { + if (pending_target) return; // haven't arrived at previous yet. + vector pos = home_position; + float omg = llFrand(1) * PI * 2; +//hmmm: make free range settable + float t_r = llFrand(1) * _FREE_RANGE; + float t_x = t_r * llCos(omg); + float t_y = t_r * llSin(omg); + go_to_target(NULL_KEY, pos + ); + motivate(); + } +} + +handle_hearing_voices(integer channel, string name, key id, string message) +{ + if (channel != PET_CHAT_CHANNEL) return; // not our channel. +//log_it("into handle voice, msg=" + message); + if (id != llGetOwner()) return; // not authorized. + // we found a command. which specific one? + if (is_prefix(message, "up")) { + // upwards bump. + enter_stay_state(); + string dist = llDeleteSubString(message, 0, 2); + if (dist == "") dist = (string)DEFAULT_BUMP_SIZE; + request_jaunt_up((integer)dist); +llOwnerSay("bumping up by " + dist); + } else if (is_prefix(message, "down")) { + // downwards bump. + enter_stay_state(); + string dist = llDeleteSubString(message, 0, 4); + if (dist == "") dist = (string)DEFAULT_BUMP_SIZE; + request_jaunt_up(-(integer)dist); +llOwnerSay("bumping down by " + dist); + } else if (is_prefix(message, "jaunt")) { + // zip to a specific place in the sim. + enter_stay_state(); + string where = llDeleteSubString(message, 0, 5); + if (where == "") { + llOwnerSay("i can't jaunt to like nowhere dude."); + return; + } + vector loc = (vector)where; + if (loc == <0.0, 0.0, 0.0>) { + llOwnerSay("jaunt locations should be in the vector format, and jaunting to <0, 0, 0> is unsupported."); + return; + } +llOwnerSay("jaunting to " + (string)loc); + jaunt_to_location(loc); + } else if (is_prefix(message, "name")) { + // toss the command portion to get our new name. + string new_name = llDeleteSubString(message, 0, 4); + if (llStringLength(new_name) > 0) { + llOwnerSay("wheeee! my new name is: " + new_name); + llSetObjectName(new_name); + show_title(); + } else { + // no data was given for the name. + llOwnerSay("my name is still " + llGetObjectName()); + } + } else { + // we support a simple translation for a few orders. + if (message == "free") message = "go home"; + + // see if we can just flip this into a menu command instead. we don't + // really care whether that works or not, since anything that doesn't work is + // a bogus command. + llMessageLinked(LINK_THIS, _COMMAND_CHANNEL, SHOW_MENU_COMMAND, + PET_MENU_NAME + HUFFWARE_PARM_SEPARATOR + message); + } +} + +////////////// + +stop_pet() +{ +//log_it("stopping pet from moving..."); + llSetPrimitiveParams([PRIM_PHYSICS, FALSE, + //]); + PRIM_PHANTOM, TRUE]); +} + +go_to_target(key av, vector pos) +{ +//log_it("told to go to target: key=" + (string)av + " pos=" + (string)pos); + TARGET_POSITION = pos; + if (av != NULL_KEY) { + vector av_size = llGetAgentSize(av); + + // if it's an object, use a different method to find its height. + if (av_size.z == 0.0) { + // use the object's height. + list box = llGetBoundingBox(KEY_OF_TARGET); + float object_height = llVecDist(llList2Vector(box, 0), llList2Vector(box, 1)); + av_size.z = object_height; + } + // adding to get pet above target. + TARGET_POSITION += < 0.0, 0.0, av_size.z / 2.0>; +//log_it("adjusted targposn: " + (string)TARGET_POSITION); + } + if (current_state == STATE_ATTACK) { + TARGET_POSITION += < 0.0, 0.0, ATTACK_HEIGHT_ABOVE_FOLLOWED_OBJECT>; + TARGET_POSITION += ; + } else { +//log_it("normal target calc"); + TARGET_POSITION += < 0.0, 0.0, DEFAULT_HEIGHT_ABOVE_FOLLOWED_OBJECT>; + TARGET_POSITION += ; + } + // trim the height a bit to keep the pet on-world. + if (TARGET_POSITION.z > 4095.0) + TARGET_POSITION.z = 4095.0; +} + +integer jaunt_responses_awaited = 0; + // the number of pending jumps that we are hoping will happen. + +integer slackness_counter; + // how many snoozes we've had waiting for our destination. + +jaunt_to_location(vector target) +{ + // send jaunt request to get us to the specified place. + llMessageLinked(LINK_THIS, JAUNT_HUFFWARE_ID, JAUNT_COMMAND, (string)target); + // add one to our counter so we know a jaunt is in progress. + jaunt_responses_awaited++; + // reset the overflow counter to recognize a new jaunt. + slackness_counter = 0; +} + +vector previous_position; + // how far away target was last time. + +motivate() +{ + // first, let's get into the right state of existence. + llSetStatus(STATUS_PHYSICS, TRUE); // we need to be able to move around here. + if (current_state == STATE_ATTACK) { + llSetStatus(STATUS_PHANTOM, FALSE); // we can bonk into things now. + } else { + llSetStatus(STATUS_PHANTOM, TRUE); // there are no obstructive contacts. + } + + vector current_pos = llGetPos(); + float distance = llVecDist(TARGET_POSITION, current_pos); + // a simple linear velocity calculation based on the object's distance. + float velocity; + if (current_state == STATE_ATTACK) { + velocity = ATTACK_BASE_VELOCITY + VELOCITY_MULTIPLIER * (distance / 10.0); + // beef the velocity up for attack mode. + velocity *= 10.0; + } else { + velocity = DEFAULT_BASE_VELOCITY + VELOCITY_MULTIPLIER * (distance / 10.0); + } + +//hmmm: make that 20 a constant + integer jump_regardless = FALSE; + if (llVecDist(current_pos, previous_position) >= 20) { + // we will always re-target when the distances have changed that much; this could mean + // the avatar is falling away. + jump_regardless = TRUE; + } +float IN_RANGE_CHANCE_TO_BAIL = 0.9; +float ATTACK_IN_RANGE_CHANCE_TO_BAIL = 0.5; + +float NEAR_RANGE_CHANCE_TO_BAIL = 0.5; +float ATTACK_NEAR_RANGE_CHANCE_TO_BAIL = 0.1; + + if (distance <= MAXIMUM_TARGETING_DISTANCE) { + // damp out the equation if the target is close enough. + if (current_state == STATE_ATTACK) velocity = ATTACK_BASE_VELOCITY; + else velocity = DEFAULT_BASE_VELOCITY; + float within_range_chance = IN_RANGE_CHANCE_TO_BAIL; + if (current_state == STATE_ATTACK) within_range_chance = ATTACK_IN_RANGE_CHANCE_TO_BAIL; + if (llFrand(1.0) <= within_range_chance) return; // do nothing; close enough. + } else if (distance <= 2.0 * MAXIMUM_TARGETING_DISTANCE) { + // we have a bit larger chance of setting a new target if the + // distance is pretty close still. + float near_range_chance = NEAR_RANGE_CHANCE_TO_BAIL; + if (current_state == STATE_ATTACK) near_range_chance = ATTACK_NEAR_RANGE_CHANCE_TO_BAIL; + if (llFrand(1.0) <= near_range_chance) return; + } + previous_position = current_pos; +//log_it("dist=" + (string)distance + " vel=" + (string)velocity); + float time = distance / velocity; + _TARGET_ID = llTarget(TARGET_POSITION, MAXIMUM_TARGETING_DISTANCE); + pending_target = TRUE; + // make sure we're in a physics mode before attempting physics changes... + llSetStatus(STATUS_PHYSICS, TRUE); + if (SITTING_AVATAR_KEY == NULL_KEY) { + // when we have nobody riding, we can look wherever we want. + llLookAt(TARGET_POSITION, 0.7, 0.5); + } else { + // if we're holding onto an avatar, we keep them pointed in a reasonable way. + vector curr_pos = llGetPos(); + vector new_lookat = ; + llLookAt(new_lookat, 0.7, 0.5); + } +//log_it("setting move to target: " + (string)TARGET_POSITION); + llMoveToTarget(TARGET_POSITION, time); + + integer panic_dist = DEFAULT_PANIC_DISTANCE; + if (current_state == STATE_ATTACK) { + panic_dist = ATTACK_PANIC_DISTANCE; + } + + // don't try to jump if we're still awaiting a jump response. + if (!jaunt_responses_awaited && (distance > panic_dist) ) { + // we need to shorten the distance to our buddy now. + jaunt_to_location(TARGET_POSITION); + } else if (jump_regardless || (distance > TARGETING_SENSOR_RANGE - 10)) { + // we are double our panic point, so jump even if still waiting for a reply. + // however, we don't want to queue up too many jaunts at a time either. + if (jaunt_responses_awaited <= 2) { + jaunt_to_location(TARGET_POSITION); + } + } + + // push the attack target if we're close enough. + if ( (current_state == STATE_ATTACK) && (distance < ATTACK_PUSH_DIST_THRESHOLD) ) { + // only decide to push if they win the lottery here. + if (llFrand(1.0) < ATTACK_PUSH_CHANCE) { + llPushObject(KEY_OF_TARGET, ATTACK_PUSH_MAGNITUDE * llRot2Up(llGetRot()), ZERO_VECTOR, FALSE); + } + } + +} + +show_title() +{ + llSetText(llGetObjectName(), <0.6, 0.3, 0.8>, 1.0); +} + +// processes a link message from some other script. +handle_link_message(integer which, integer num, string msg, key id) +{ +//log_it("got msg=" + msg + " id=" + (string)id); + if (num == JAUNT_HUFFWARE_ID + REPLY_DISTANCE) { +//log_it("link jaunt reply"); + if (msg == JAUNT_COMMAND) { + jaunt_responses_awaited--; // one less response being awaited. + if (jaunt_responses_awaited < 0) { + log_it("erroneously went below zero for jaunt responses!"); + jaunt_responses_awaited = 0; + } + // unpack the reply. + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + integer last_jaunt_was_success = (integer)llList2String(parms, 0); + vector posn = (vector)llList2String(parms, 1); +//log_it("got a reply for a jaunt request, success=" + (string)last_jaunt_was_success + " posn=" + (string)posn); + } + return; + } + if (num != MENUTINI_HUFFWARE_ID + REPLY_DISTANCE) return; // not for us. +//log_it("menu reply"); + react_to_menu(which, num, msg, id); +} + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return (llSubStringIndex(compare_with, prefix) == 0); } + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +initialize() +{ + show_title(); + llSetPrimitiveParams([PRIM_PHYSICS, FALSE, + //]); + PRIM_PHANTOM, TRUE]); + llSitTarget(SIT_POSITION, llEuler2Rot(SIT_ROTATION * DEG_TO_RAD)); + llSetSitText(SIT_TEXT); + llSetBuoyancy(1.0); + _OWNER = llGetOwner(); + _FOLLOW_KEY = []; + _FOLLOW_NAME = []; + current_state = STATE_FREE; + TARGET_POSITION = llGetPos(); + llSetTimerEvent(PERIODIC_INTERVAL); + slackness_counter = 0; + _COMMAND_CHANNEL = random_channel(); + _FOLLOW_CHANNEL = random_channel(); + _COMMAND_CHANNEL = random_channel(); + llListen(PET_CHAT_CHANNEL, "", llGetOwner(), ""); + home_position = llGetPos(); // start in a known place. +} + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + initialize(); + } + + on_rez(integer param) { llResetScript(); } + + touch_start(integer num_detected) { + show_title(); +//change title to show menuing state? + if (_OWNER == llDetectedKey(0)) { + // show our menu here. + show_menu(PET_MENU_NAME, _COMMAND_MESSAGE, SUPPORTED_COMMANDS, _COMMAND_CHANNEL); + } + } + + link_message(integer sender, integer num, string msg, key id) { + handle_link_message(sender, num, msg, id); + } + + sensor(integer num_detected) { +//log_it("sensor found " + llDetectedName(0)); + handle_sensor(num_detected); + } + + no_sensor() { +//use another means to find the avatar? + } + + at_target(integer number, vector targetpos, vector ourpos) { +//log_it("at target"); + llTargetRemove(_TARGET_ID); + pending_target = FALSE; + llStopMoveToTarget(); + } + + not_at_target() { +//log_it("not at target"); + + } + + changed(integer change) { + if (change & CHANGED_LINK) { + key av = llAvatarOnSitTarget(); + if (SITTING_AVATAR_KEY != NULL_KEY) { + if (av == NULL_KEY) { + llStopAnimation(SIT_ANIMATION); + SITTING_AVATAR_KEY = NULL_KEY; + } + } else { + if (av != NULL_KEY) { + SITTING_AVATAR_KEY = av; + llRequestPermissions(SITTING_AVATAR_KEY, PERMISSION_TRIGGER_ANIMATION); +// we wish we could make the avatar a phantom here, but that's not allowed. + } + } + } + } + + run_time_permissions(integer perm) { + key perm_key = llGetPermissionsKey(); + if (perm_key == SITTING_AVATAR_KEY) { + if (perm & PERMISSION_TRIGGER_ANIMATION) { + list anms = llGetAnimationList(SITTING_AVATAR_KEY); + integer i; + for (i = 0 ; i < llGetListLength(anms) ; i++) { + llStopAnimation(llList2Key(anms, i)); + } + llStartAnimation(SIT_ANIMATION); + } + } + } + + timer() { + handle_timer(); + } + + listen(integer channel, string name, key id, string message) { + handle_hearing_voices(channel, name, id, message); + } + +} + +// attributions: +// this script is based (a lot!) on the "pet" script that might +// have been written by kazumasa loon. there was no attribution +// of author in the script, but the creator was kazumasa. thanks dude! +// +// that being said, the script was redone a lot by fred huffhines, +// mainly in the following areas: +// +// march or april 2008: added teleport capability to script. pet will now attempt +// to keep up with the owner during follow mode by teleporting to her. +// +// may 2008: added ipc menu system. now menus are dealt with by the huffware +// menu system, removing a lot of code from this script. + diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/huff-search_brainiac_v46.7.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/huff-search_brainiac_v46.7.lsl new file mode 100755 index 00000000..a5c10dce --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/huff-search_brainiac_v46.7.lsl @@ -0,0 +1,1248 @@ + +// huffware script: huff-search brainiac, by fred huffhines +// +// this script is one portion of a search system. it is the brain for an object +// that can find nearby objects by a partial name match. this script should be +// located in the root primitive. it also requires an up-to-date jaunting library +// in the same primitive. see the (hopefully) enclosed documentation for more details. +// more implementation notes are stored at the back of the file. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// global settings that can be very useful to change... + +integer MAX_MATCHES = 17; + // by default we try to match one object per rod so we can point at them. + +float MAX_SPIRAL_RADIUS = 72.0; + // the farthest that the object will travel from its home while searching. + // this is an important factor in the searchbert's range of vision; the other is + // the sensor range, below. + // ensure that it has access to the lands within this range or it might get stuck. + // you can also set the TRAVERSE_LANDS flag to false to make for safer searches when + // surrounded by a lot of banned lands. + +float SENSOR_MAX_RANGE = 96.0; + // the maximum range that we try to sense objects at. this is distinct from the spiral + // radius because often the range of movement is constrained by land conditions, where + // one might still want a large sensor radius to see as many things as possible in range. + // note though that searches will keep matching the things nearest to them if neither + // the position nor the angle nor object positions have changed. + +float SPIRAL_LOOPS = 7; + // how many loops there are in the spiral. this is measured by how many times the + // spiral cuts across the positive x-axis (that is, the zero angle vector) from + // the center of the spiral to the radius (that is, from <0, 0, 0> to ). + +integer TOTAL_STEPS = 42; + // how many positions within the spiral will the object travel to? the first step + // is step 0, which is the center of the spiral. positions 1 through TOTAL_STEPS + // spiral outward from the zero position, where the distance from the center at + // TOTAL_STEPS should be MAX_SPIRAL_RADIUS or less. + +integer MAX_SPIRAL_TRAVERSALS = 1; + // how many different spirals are we allowed for the same search? this will let the + // search engine cover more ground than with just one spiral, to allow different + // objects to be exposed. + +integer HUFF_SEARCH_CHAT_CHANNEL = 0; + // the channel where the object listens for commands. the default is to use open + // chat for this, but some people may want it more specific. + +// this flag is important to keep as false for wright plaza and other areas with script traps +// (where the perms allow objects to enter, but then their scripts are stopped dead). +integer TRAVERSE_LANDS = FALSE; + // if this is false, then the searchbert will stick to the land owner's land where it started. + // but if it's true, then searchbert will cross into other people's lands too, but if there + // are some weird border or permission conditions about object re-entry or scripts being able + // to run, that can be quite problematic. + +integer TRAVELLING_UPWARDS = FALSE; + // if this is true, the searchbert will climb in the vertical direction also. + +float MAX_UPWARDS_DISTANCE = 30.0; + // the farthest away that the searchbert will fly during an upwards search. + +float SEARCH_ROD_ANGULAR_SWEEP = PI_BY_TWO; + // angular arc of the sensor cone, placeholder. + +////////////// + +// constants that are not configurable in a notecard... + +integer DEBUGGING = FALSE; // set to true to make the script noisier. + +////////////// + +// global constants that aren't as useful to change... + +float MINIMUM_HEIGHT_ABOVE_GROUND = 0.14; + // the closest we allow the searcher to get to the ground. any lower and our search + // rods might be dragging around under ground. + +float FAST_TIMER_INTERVAL = 0.12; + // the time between jumps when we're doing our search spiral. this is the fastest + // the object can spin around its spiral trajectory, but it's more limited than by + // just this number; llSetPrimitiveParams is used for jaunting and it has a delay of + // 0.2 seconds built into it. + +integer NORMAL_TIMER_INTERVAL = 14; + // how frequently we check for things to do, in seconds. + +float STARTUP_TIME_ALLOWED = 50.0; // number of seconds before declaring a timeout. + +integer MAX_STEP_SNOOZES = 81; + // number of timer hits to allow before giving up on jaunt. + +float SNOOZE_BEFORE_RESET = 300; + // number of seconds before a scan will automatically reset. hopefully this is + // enough time for the user to follow the trail to the detected object. + +// imported interfaces below... + +// huff-search pointer API: +////////////// +// do not redefine these constants. +integer HUFF_SEARCH_POINTER_HUFFWARE_ID = 10032; + // the unique id within the huffware system for this script. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +////////////// +string HUFF_SEARCH_RESET = "#reset#"; + // returns the script to its starting state. +string HUFF_SEARCH_POINT_PARTY = "#point_particles#"; + // aim at an object and show a particle stream leading to it. +string HUFF_SEARCH_JUST_POINT = "#just_point#"; + // aim at an object, but don't do any particles. +string HUFF_SEARCH_SENSEI = "#sensor#"; + // set up a sensor request for a search pattern. pings will cause + // the pattern to be sought in names of nearby objects. the parameters are: + // (1) the maximum range for the sensor, (2) the arc angle to use in sensing, + // (3) the search pattern to look for in object names, (4) the maximum number + // of matches to look for. +string HUFF_SEARCH_STOP_SENSE = "#stop_sensor#"; + // turn off the sensor but don't totally reset. +string HUFF_SEARCH_PING = "#ping#"; + // cause the searcher to actively sensor ping the targets. +string HUFF_SEARCH_MATCH_EVENT = "#match#"; + // fired at the root prim when matches are found for the search term. + // the payload is a list of matched item pairs [item key, location]. +////////////// + +// the armature button pushing API. +// (we have subclassed the simple button pushing API for searchbert armature.) +////////////// +integer BUTTON_PUSHER_HUFFWARE_ID = 10029; + // a unique ID within the huffware system for this script. +////////////// +string BUTTON_PUSHED_ALERT = "#btnp"; + // this event is generated when the button is pushed. the number parameter will be + // the huffware id plus the reply distance. the id parameter in the link message will + // contain the name of the button that was pushed. +////////////// +string CHECK_ARMS_BUTTON_NAME = "^checkarms"; + // this is the signal given to the armature script that it should check the + // number of arms present on searchbert. if the number is fine, it will + // push the arms are good button back at the brainiac (using the button + // push api plus reply distance). +string ARMS_ARE_GOOD_BUTTON_NAME = "^goodarmz"; + // the event sent back by the searchbert armature when all arms are ready + // to go. +string PROBLEM_WITH_MY_THUMBS_BUTTON_NAME = "^ouch"; + // a problem was noticed with the number of arms and we could not fix it. + // the brain needs to try reconfiguring again. +////////////// + +// searchbert menus API. +////////////// +// do not redefine these constants. +integer SEARCHBERT_MENUS_HUFFWARE_ID = 10034; + // the unique id within the huffware system for this script. +////////////// +string SM_CONFIGURE_INFO = "#sm-info#"; + // sets important information this script will use, such as (1) the channel for listening. +string SM_POP_MAIN_MENU_UP = "#sm-main#"; + // causes the main menu to be displayed. this requires an avatar name and avatar key for the + // target of the menu. +////////////// +string SM_EVENT_MENU_CLICK = "#sm-clik#"; + // the user has requested a particular menu item that this script cannot fulfill. the + // event is generated back to the client of this script for handling. it will include (1) the + // menu name in question, (2) the item clicked, (3) the avatar name, and (4) the avatar key. +////////////// + +// card configurator link message API: +////////////// +// do not redefine these constants. +integer CARD_CONFIGURATOR_HUFFWARE_ID = 10042; + // the unique id within the huffware system for the card configurator script to + // accept commands on. this is used in llMessageLinked as the num parameter. +////////////// +string BAD_NOTECARD_TEXT = "*badcrd*"; + // the sign that we hated the notecards we found, or there were none. +string FINISHED_READING_NOTECARDS = "**finished**"; + // the sign that we are done plowing through the card we found. +string BEGIN_READING_NOTECARD_COMMAND = "#read-cfg#"; + // requests that the configurator find a good notecard and read its contents. + // it should send the contents via the alert below. first parm is the signature and + // second is the wrapped list of valid item prefixes. +string READ_PARTICULAR_NOTECARD_COMMAND = "#read-note#"; + // requests that the configurator find a good notecard and read its contents. + // it should send the contents via the alert below. first two parms are the same as + // begin reading notecard, and the third parameter is the name of the specific notecard. +string CARD_CONFIG_RECEIVED_ALERT = "#cfg-event-upd#"; + // this message is sent when the configurator has found some data updates or has finished + // reading the configuration file. +////////////// + +// jaunting library API: +////////////// +// do not redefine these constants. +integer JAUNT_HUFFWARE_ID = 10008; + // the unique id within the huffware system for the jaunt script to + // accept commands on. this is used in llMessageLinked as the num parameter. +////////////// +// commands available via the jaunting library: +string JAUNT_COMMAND = "#jaunt#"; + // command used to tell jaunt script to move object. pass a vector with the location. +//string FULL_STOP_COMMAND = "#fullstop#"; + // command used to bring object to a halt. +//string REVERSE_VELOCITY_COMMAND = "#reverse#"; + // makes the object reverse its velocity and travel back from whence it came. +//string SET_VELOCITY_COMMAND = "#setvelocity#"; + // makes the velocity equal to the vector passed as the first parameter. +//string JAUNT_UP_COMMAND = "#jauntup#"; +//string JAUNT_DOWN_COMMAND = "#jauntdown#"; + // commands for height adjustment. pass a float for number of meters to move. +//string JAUNT_LIST_COMMAND = "#jauntlist#"; + // like regular jaunt, but expects a string in jaunt notecard format with vectors. + // the second parameter, if any, should be 1 for forwards traversal and 0 for backwards. +// +////////////// + +// global variables used in the script. + +// configuration variables... +integer all_setup_finished = FALSE; // have we read our configuration yet? +integer configuration_pending = FALSE; // true if the setup process (config and arms) is still going on. +integer listening_handle = 0; // tracks our handle for listening to commands. + +// matching variables... +string global_target_name = ""; // the name of the object being sought. + +list global_matches_found; // a list of keys that match the specified search terms. +list global_positions_found; // matches list of positions for the detected objects. + +// jaunting variables... + +vector global_home_posn; // the location where the search bot is located. +vector last_safe_target; // the last place we jaunted to that was safe. + +integer next_step_snoozes_left; // pauses allowed before deciding jaunt will not respond. + +integer jaunt_responses_awaited = 0; // true when a jaunt is pending. +integer last_jaunt_was_success = FALSE; // result of jaunting received by message. + +vector current_rotation = <0.0, 0.0, 0.0>; + // the current rotation around each axis for object. + +// spiral variables... + +integer global_current_step = 0; // the current position in the search spiral. +float spiral_start_angle = 0.0; // how many radians to offset spiral by for this pass. +integer global_current_pass = 0; // which spiral number are we working on? + +integer tried_jump_homeward = FALSE; + // when retracing to home, this records if we already tried the jump home that should + // come in between the spiral jaunt. + +integer retracing_steps = FALSE; + // this is true when we are trying to get back home after finishing our spirals. + +// object maintenance variables... + +integer reset_for_next_timer = FALSE; // true when next timer hit should do a reset. + +integer running_a_search = FALSE; // true if the object is seeking matches. + +////////////// + +// constants that should not be messed with. + +float MAX_SLACK_DISTANCE = 0.01; // how close we need to be to a target. + +integer ALL_SEEKER_ALERT = -1; // communicates with all search rods. + +integer last_time_ordered_stop_sensing = 0; + // tracks when we last tried to order the search rods to stop sensing matches. + +// jaunting variables... + +vector last_jaunt_target; + // where we're currently headed. this variable is mostly for record keeping, so we want + // to update it whenever we jaunt someplace. + +////////////// + +// main functions for getting work done as the searchbert... + +// pointing that doesn't use particles, but just aims a search rod. +aim_at_position(integer which_seeker, vector targetPosition) { + list paramList = [targetPosition]; + request_from_seeker(which_seeker, llDumpList2String(paramList, + HUFFWARE_PARM_SEPARATOR), HUFF_SEARCH_JUST_POINT); +} + +// tells the rod at "which_seeker" link to do a sensor scan. +start_sensing(integer which_seeker, float max_range, float arc_angle, string search_pattern, + integer matches_sought) +{ + list paramList = [max_range, arc_angle, search_pattern, matches_sought]; + request_from_seeker(which_seeker, llDumpList2String(paramList, + HUFFWARE_PARM_SEPARATOR), HUFF_SEARCH_SENSEI); +} + +// tells all of the seeker search rods to stop sensing matches. +stop_sensing() +{ + // make sure that we are not just blasting this order over and over. + if (llGetUnixTime() != last_time_ordered_stop_sensing) { + last_time_ordered_stop_sensing = llGetUnixTime(); + request_from_seeker(ALL_SEEKER_ALERT, "", HUFF_SEARCH_STOP_SENSE); + } +} + +// tell the seeker arms to find what we asked them about. +request_ping() { request_from_seeker(ALL_SEEKER_ALERT, "", HUFF_SEARCH_PING); } + +////////////// + +// teleports to the vector specified. this invokes a method in the jaunting library +// which will return its result asynchronously. thus we can't just expect that we +// have arrived at the target by the end of this function; we need to get back the +// IPC message in linked_message. +jaunt_to_target(vector target) +{ + if (!TRAVERSE_LANDS) { + // this is an important restriction since we don't want to get trapped in weird + // land perm screwups, like not being able to re-enter the land where we started, + // if that parcel option is set. + if (llGetLandOwnerAt(llGetPos()) != llGetLandOwnerAt(target)) { + return; + } + } + + // reset our snoozer count, since we're doing a new jaunt now. + next_step_snoozes_left += MAX_STEP_SNOOZES; + jaunt_responses_awaited++; + // record the current target. + last_jaunt_target = target; + + float minimum_rod_angle_addition = 0.2; + float maximum_rod_angle_addition = 10.8; + // add small random amounts to the current rotation to expose more matches. + current_rotation += + <(minimum_rod_angle_addition + llFrand(maximum_rod_angle_addition)) * DEG_TO_RAD, + (minimum_rod_angle_addition + llFrand(maximum_rod_angle_addition)) * DEG_TO_RAD, + (minimum_rod_angle_addition + llFrand(maximum_rod_angle_addition)) * DEG_TO_RAD>; + rotation new_rot = llEuler2Rot(current_rotation); +// log_it("new rot to " + (string)new_rot + " based on " + (string)current_rotation); + llMessageLinked(LINK_THIS, JAUNT_HUFFWARE_ID, JAUNT_COMMAND, + (string)target + HUFFWARE_PARM_SEPARATOR + (string)new_rot); +} + +// provides the position on a spiral that has "loops" total loops (counted by +// how many times a line from the center to the outer "radius" is crossed). +// this provides an x and y offset from 0,0 for where the spiral should be if +// one were to walk it in "total_steps" (where the center is step 0). this is +// given for the current "step" one is at. the "start_angle" is the number of +// radians to start at for the spiral, to allow different areas to be traversed. +vector trace_spiral(float loops, float radius, integer total_steps, integer step, + float start_angle) +{ + float total_radians = TWO_PI * loops; + float angle_per_step = total_radians / (float)total_steps; + float distance = radius / (float)total_steps * (float)step; + float current_angle = start_angle + angle_per_step * (float)step; + float current_z = 0; + if (TRAVELLING_UPWARDS) { + // add in the vertical distance for this step. + current_z = MAX_UPWARDS_DISTANCE / total_steps * step; + } + return ; +} + +// this describes the list of matches in local chat. +chat_about_matches() +{ + integer len = llGetListLength(global_matches_found); + if (!len) { + llSay(0, global_target_name + " not found within a distance of " + + (string) (SENSOR_MAX_RANGE + MAX_SPIRAL_RADIUS) + " meters from here."); + return; + } + string addition; + if (len != 1) addition = "es"; + string match_description = (string)len + " match" + addition + " for search " + + "pattern \'" + global_target_name + "\':\n"; + integer i; + // only show a maximum number of matches as we have seeker objects. + if (len > MAX_MATCHES) len = MAX_MATCHES; + for (i = 0; i < len; i++) { + if (llStringLength(match_description) > 350) { + llSay(0, "\n" + match_description); + match_description = ""; + } else if (i != 0) match_description += "\n"; + key targetKey = llList2Key(global_matches_found, i); + vector targetPos = llList2Vector(global_positions_found, i); + match_description += llKey2Name(targetKey) + + " @ " + vector_to_string(targetPos) + + " [" + (string) targetKey + "]"; + } + llSay(0, "\n" + match_description); +} + +// once we accumulate a set of matches, we want to show them off. +show_matches() +{ + llSetRot(llEuler2Rot(ZERO_VECTOR)); // set the object to point at the zero vector. + reset_rod(ALL_SEEKER_ALERT); // clean up any current pointing first. + chat_about_matches(); // say where the matches are. + + integer i; + integer which_locater = 0; // which pointer to use. + // only show a maximum number of matches as we have seeker objects. + integer len = llGetListLength(global_matches_found); + if (len > MAX_MATCHES) len = MAX_MATCHES; + for (i = 0; i < len; i++) { + key targetKey = llList2Key(global_matches_found, i); + vector targetPos = llList2Vector(global_positions_found, i); + point_at_with_particles(which_locater++, targetKey, targetPos); + } +} + +// makes sure that a target we are given is above ground. this used to do a +// lot more checking, but really being above ground is the most important thing +// for us to check at this level of the code. +vector clean_target(vector to_clean) +{ + vector to_return = to_clean; + float ground_height = llGround(to_return - llGetPos()); +//log_it("ground height here: " + (string)ground_height); + + // we'll adjust the basic ground height by adding in the object's height. + list bounds = llGetBoundingBox(llGetKey()); +//hmmm: we have found a bug in opensim in that the bounding box only covers the root prim. +// this needs to be reported. + vector min = llList2Vector(bounds, 0); + vector max = llList2Vector(bounds, 1); +//log_it("calcd: min=" + (string)min + " max=" + (string)max); + +//real math float distance_to_add = llVecDist(<0, 0, min.z>, <0, 0, max.z>) / 2.0; +//below is bogus math for opensim currently. + float distance_to_add = llVecDist(<0, 0, min.z>, <0, 0, max.z>) * 1.1; + +//log_it("distance_to_add: " + (string)distance_to_add); + if (to_return.z - MINIMUM_HEIGHT_ABOVE_GROUND <= ground_height + distance_to_add) { + // patch up the height to be above ground. + to_return.z = ground_height + distance_to_add + MINIMUM_HEIGHT_ABOVE_GROUND; + } + return to_return; +} + +// shows all the matches by pointing our seekers at them and emitting a targeted +// trail of particles. this method should only be called after the final jaunt +// back to home has occurred. +show_off_what_was_found() +{ + // show the positions of what was found. + show_matches(); + llSetTimerEvent(SNOOZE_BEFORE_RESET); + reset_for_next_timer = TRUE; +} + +// stops the device from travelling around and asking for help from the search rods. +integer finish_spiral_traversal() +{ +//log_it("got to finish spiral..."); + stop_sensing(); // turn off all sensors. + + // are we close enough to home to declare victory? + if (llVecDist(llGetPos(), global_home_posn) > MAX_SLACK_DISTANCE) { + if (!tried_jump_homeward) { + // there are enough here; we completed early. + tried_jump_homeward = TRUE; +//log_it("finish spiral sees us as too far from home, jaunting there."); + jaunt_to_target(global_home_posn); + return FALSE; + } else { + // we already tried a jump home. dang, let's try spiraling. +//log_it("finish spiral failed jaunt home, continuing spiral."); + tried_jump_homeward = FALSE; // reset that we tried this. + return TRUE; + } + } + +//log_it("finish spiral success, showing off matches"); + + // this object has done enough spiraling. + llSetTimerEvent(NORMAL_TIMER_INTERVAL); // turn off jump timer. + running_a_search = FALSE; // reset our state since the search is done. + show_off_what_was_found(); + return FALSE; +} + +// our timer handling function; this will move the search engine to the next place on +// the current spiral (or to the start of the next spiral) when it is called. +// if we're not currently running a search, then it will just periodically update the +// sub-prims to make sure they have the latest versions. +handle_timer() +{ + if (!all_setup_finished) { + complain_about_config(!configuration_pending); + return; // should not get here. + } + + if (reset_for_next_timer) { + // this timer intends a reset so we don't just keep pointing forever. + llSay(0, "Timed Out: resetting matches now."); + reset_search(); + return; + } + + if (!running_a_search) { +//log_it("exiting timer since not searching."); + return; + } + +//llSay(0, "next search move!"); + + // make sure we don't allow too many pauses while awaiting a jaunt completion. + if (next_step_snoozes_left-- <= 0) { + log_it("waiting to arrive failed! ran out of snoozes trying to get to " + + (string)last_jaunt_target); + } else if (jaunt_responses_awaited) { + // not ready to do next step yet. + return; + } + if (next_step_snoozes_left < 0) { +//log_it("got below zero in next step snoozes!"); + next_step_snoozes_left = 0; + } + + if (llGetListLength(global_matches_found) < MAX_MATCHES) { + // tell the sensor arms to look for stuff right here. + request_ping(); + } + + // make sure we're not already done finding enough objects. + if (llGetListLength(global_matches_found) >= MAX_MATCHES) retracing_steps = TRUE; + + // check whether we're trying to get home again, rather than moving forward. + if (retracing_steps) { +//log_it("retrace steps in timed move."); + integer keep_going = finish_spiral_traversal(); + if (!keep_going) return; + } + + // see if the object has reached the end of its tether and should report + // home with results. we do this by taking a spiral pass outwards from home + // and a return pass to get back to home. if we've used up all our spiral + // traversals, then it's time to stay home and show the matches. + integer done_with_this_spiral = FALSE; + if (global_current_pass % 2 == 0) { + // even passes are outward bound ones. we're done when we've gotten to TOTAL_STEPS. + if (global_current_step++ > TOTAL_STEPS) done_with_this_spiral = TRUE; + } else { + // odd passes are for returning to home. those are done when they hit zero. + if (global_current_step-- <= 0) done_with_this_spiral = TRUE; + } + + if (done_with_this_spiral) { + // now evaluate whether we're totally done or just need to move to next spiral, + // whether inward or outward. + if (++global_current_pass < 2 * MAX_SPIRAL_TRAVERSALS) { + // just step to the next spiral. +//log_it("moving to next spiral"); + if (global_current_pass % 2 == 0) { + // even passes are outward bound ones. + global_current_step = 0; + // add some different angular rotation to get more matches for this spiral. + spiral_start_angle += TWO_PI / (MAX_SPIRAL_TRAVERSALS + 1); + } else { + // odd passes are for returning to home. + global_current_step = TOTAL_STEPS; + } +//hmmm: not currently resetting that angle for next search. + } else { +//log_it("done with spiral mode, now retracing steps."); + // we finished the search pattern. + retracing_steps = TRUE; + global_current_pass--; // try spiraling back. + global_current_step = TOTAL_STEPS; + return; + } + } + + // normal activity here--pick the next place on the spiral to look for objects. + vector new_posn = trace_spiral(SPIRAL_LOOPS, MAX_SPIRAL_RADIUS, + TOTAL_STEPS, global_current_step, spiral_start_angle); +//log_it("next step " + (string)global_current_step + " to " + (string)new_posn); + // go to that location now, or rather, when the jaunting library gets the message. + vector new_target = clean_target(new_posn + global_home_posn); + jaunt_to_target(new_target); +} + +// makes sure we are ready to run. TRUE is returned if we're good. +integer test_health() +{ + if (!configuration_pending && !all_setup_finished) { + llSay(0, "Configuration is not good yet; retrying."); + return FALSE; + } + return TRUE; +} + +// deals with the jaunter telling us about a completed jump. +process_jaunt_response(string str) +{ + jaunt_responses_awaited--; // one less response being awaited. + if (jaunt_responses_awaited < 0) { + if (DEBUGGING) + log_it("erroneously went below zero for jaunt responses!"); + jaunt_responses_awaited = 0; + } + // unpack the reply. + list parms = llParseString2List(str, [HUFFWARE_PARM_SEPARATOR], []); + last_jaunt_was_success = (integer)llList2String(parms, 0); + vector posn = (vector)llList2String(parms, 1); +//log_it("got a reply for a jaunt request, success=" + (string)last_jaunt_was_success + " posn=" + (string)posn); + if (last_jaunt_was_success) { + last_safe_target = posn; + } else { + // we had a problem getting to the expected destination, so go to the last place we were + // completely safely able to reach. we should have no problem returning there, since that's + // where we should have been coming from when we failed to reach the intended destination. +//log_it("bkwd retracing to " + (string)last_safe_target); + jaunt_to_target(last_safe_target); + } +} + +reset_search() +{ + reset_for_next_timer = FALSE; + + llSetTimerEvent(NORMAL_TIMER_INTERVAL); // back to slow timer hits. + reset_rod(ALL_SEEKER_ALERT); // reset the pointers to camber position. + llSetRot(llEuler2Rot(ZERO_VECTOR)); // set the object to point at the zero vector. + + // set the home position to wherever we happen to be right now. + global_home_posn = clean_target(llGetPos()); + llSetPos(global_home_posn); // get us off the ground. + last_safe_target = global_home_posn; + + global_current_step = 0; + global_current_pass = 0; + global_matches_found = []; + global_positions_found = []; + next_step_snoozes_left = 4; // reset to small num, since this is additive. + reset_for_next_timer = FALSE; + running_a_search = FALSE; + tried_jump_homeward = FALSE; + retracing_steps = FALSE; + jaunt_responses_awaited = 0; + current_rotation = <0.0, 0.0, 0.0>; + // fix a global 'constant' that can't be pre-initiatlized in LSL. + SEARCH_ROD_ANGULAR_SWEEP = (PI_BY_TWO / 4.0); + // PI_BY_TWO / 4 is 22.25 degrees, which when turned into a sensor cone + // will be a range of 45 degrees around the zero vector. + +} + +initialize() +{ + llSay(0, "Initializing... this may take a few seconds."); + auto_retire(); + llSleep(0.2); // we ensure part of the claim above by waiting for sub-scripts to start. + request_configuration(""); +} + +// complete the initialization once we know our configuration. +finish_initializing() +{ + all_setup_finished = TRUE; // cancel the time-out checker. + configuration_pending = FALSE; + + // in case we already have some arms, clean up their pointing states. + reset_search(); + // set the position of rotation back to zero. + llSetRot(<0.0, 0.0, 0.0, 1.0>); + // listen for commands from our chat channel. + listening_handle = llListen(HUFF_SEARCH_CHAT_CHANNEL, "", NULL_KEY, ""); + + llSay(0, "Running and ready to search. Touch for more instructions."); + + llSetTimerEvent(NORMAL_TIMER_INTERVAL); +} + +// this points the rods at all angles we possibly can to get the best coverage. +// if show_particles is true, then a demo mode is used that points at the positions +// that the rods are aiming at. +skew_rod_angles(integer show_particles) +{ + reset_rod(ALL_SEEKER_ALERT); + // point everyone straight up for the moment. + float radius = 4.0; // arbitrary distance of target we're aiming at. + integer which_child; + integer total_searchers = llGetNumberOfPrims() - 1; + float angle_per_step = TWO_PI / (float)total_searchers; + // use a circle for the x and y coordinates. + for (which_child = 0; which_child < total_searchers; which_child++) { + vector spinner = < + radius * llCos((float)which_child * angle_per_step), + radius * llSin((float)which_child * angle_per_step), + radius * llCos((float)which_child * angle_per_step / 2.0)>; + vector target_location = llGetPos() + spinner; + if (!show_particles) { + aim_at_position(which_child, target_location); + } else { + point_at_with_particles(which_child, NULL_KEY, target_location); + } + } +} + +// if "noisy" is true, this says out loud that the searchbert is busy. +integer check_if_search_pending(integer noisy) +{ + if (running_a_search) { + if (noisy) llSay(0, "A search is still pending. Please wait for that to finish."); + return TRUE; // still working on a search. + } + return FALSE; // not busy. +} + +// processes a message coming back from a search rod or from the jaunter library. +handle_link_message(integer which, integer num, string msg, key id) +{ + if ( (num == CARD_CONFIGURATOR_HUFFWARE_ID + REPLY_DISTANCE) + && (msg == CARD_CONFIG_RECEIVED_ALERT) ) { + // the first element of the list will still be the notecard name. + consume_configuration(llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], [])); + return; + } + + if ( (num == BUTTON_PUSHER_HUFFWARE_ID + REPLY_DISTANCE) + && (msg == BUTTON_PUSHED_ALERT) ) { + if (id == ARMS_ARE_GOOD_BUTTON_NAME) { + finish_initializing(); + } else if (id == PROBLEM_WITH_MY_THUMBS_BUTTON_NAME) { + complain_about_config(TRUE); + } + return; + } + + if (num == JAUNT_HUFFWARE_ID + REPLY_DISTANCE) { + if (msg == JAUNT_COMMAND) { + process_jaunt_response(id); + } + return; + } + + list parms; + + if ( (num == SEARCHBERT_MENUS_HUFFWARE_ID + REPLY_DISTANCE) && (msg == SM_EVENT_MENU_CLICK) ) { + parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + process_menu_choice(llList2String(parms, 0), llList2String(parms, 2), llList2String(parms, 3), + llList2String(parms, 1)); + return; + } + + if (num != HUFF_SEARCH_POINTER_HUFFWARE_ID + REPLY_DISTANCE) return; // not for us. + if (msg != HUFF_SEARCH_MATCH_EVENT) return; // also not for us. + + // make sure this is not already redundant, before doing a costly list search. + if (llGetListLength(global_matches_found) >= MAX_MATCHES) { + // we have enough already. + stop_sensing(); + return; + } +//hmmm: searchbert arms are still backwards. + +//log_it("unpacking " + (string)llStringLength(id) + " byte string."); + // fluff out the list back from the encoded string. + parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); +//log_it("received " + (string)llGetListLength(parms) + " element list from string."); + + while (llGetListLength(parms) > 1) { + // unpack the match that one of our seekers found for us. + key found = (string)llList2String(parms, 0); + if (find_in_list(global_matches_found, found) < 0) { + vector posn = (vector)llList2String(parms, 1); + // that key wasn't already listed, so we can drop it in the list. + global_matches_found += found; + global_positions_found += posn; + } + // truncate the list by removing those two pieces we already handled. + parms = llDeleteSubList(parms, 0, 1); + if (llGetListLength(global_matches_found) >= MAX_MATCHES) { + // we can bail out of the list processing now; we have enough already. + parms = []; + } + } +} + +// uses the configuration item as the value of one of our variables, if we can figure out +// the name involved. +apply_variable_definition(string var, string value) +{ +//log_it("[" + var + "=" + value + "]"); + if (var == "max_matches") { + MAX_MATCHES = (integer)value; + } else if (var == "max_spiral_radius") { + MAX_SPIRAL_RADIUS = (float)value; + } else if (var == "sensor_max_range") { + SENSOR_MAX_RANGE = (float)value; + } else if (var == "huff_search_chat_channel") { + // we have to hook up our ears to the new channel here. + if (listening_handle) llListenRemove(listening_handle); + HUFF_SEARCH_CHAT_CHANNEL = (integer)value; + listening_handle = llListen(HUFF_SEARCH_CHAT_CHANNEL, "", NULL_KEY, ""); + } else if (var == "traverse_lands") { + TRAVERSE_LANDS = (integer)value; + } else if (var == "spiral_loops") { + SPIRAL_LOOPS = (integer)value; + } else if (var == "total_steps") { + TOTAL_STEPS = (integer)value; + } else if (var == "max_spiral_traversals") { + MAX_SPIRAL_TRAVERSALS = (integer)value; + } else if (var == "search_rod_angular_sweep") { + SEARCH_ROD_ANGULAR_SWEEP = (float)value; + } else if (var == "travelling_upwards") { + TRAVELLING_UPWARDS = (integer)value; + } else if (var == "max_upwards_distance") { + MAX_UPWARDS_DISTANCE = (float)value; + } else { + if (DEBUGGING) + log_it("unknown variable '" + var + "' tried to define value = " + value); + } +} + +// we have to consume the configuration in digestible chunks, since the pieces may +// be too large for sending in link messages. +consume_configuration(list config_chunk) +{ + string notecard_name = llList2String(config_chunk, 0); + if (notecard_name == BAD_NOTECARD_TEXT) { + complain_about_config(FALSE); + return; + } else if (notecard_name == FINISHED_READING_NOTECARDS) { + // we're ready to make use of our new configuration now. + if (DEBUGGING) + log_it("Configuration has been read, need validation of limbs."); + configuration_pending = FALSE; // no longer waiting for notecard config. + // configure the menu system to know what channel to talk about. + llMessageLinked(LINK_THIS, SEARCHBERT_MENUS_HUFFWARE_ID, SM_CONFIGURE_INFO, + wrap_parameters([HUFF_SEARCH_CHAT_CHANNEL])); + // now check our arms to make sure they're all ready. + llMessageLinked(LINK_THIS, BUTTON_PUSHER_HUFFWARE_ID, BUTTON_PUSHED_ALERT, CHECK_ARMS_BUTTON_NAME); + return; + } + + integer sandy; + // scan the configuration items two at a time, but skip the notecard name at slot zero. + for (sandy = 1; sandy < llGetListLength(config_chunk); sandy += 2) { + string var = llList2String(config_chunk, sandy); + string value = llList2String(config_chunk, sandy + 1); + apply_variable_definition(var, value); + } + // clear the list out now that we've eaten its contents. + config_chunk = []; +} + +////////////// + +// sends a message to the "which_seeker" search object. the "parameters" should be a +// string-ized list of parameters. the "command" is the specific action requested. +request_from_seeker(integer which_seeker, string parameters, string command) +{ + integer linkNumber = which_seeker + 2; + if (which_seeker == ALL_SEEKER_ALERT) linkNumber = LINK_ALL_OTHERS; + llMessageLinked(linkNumber, HUFF_SEARCH_POINTER_HUFFWARE_ID, command, parameters); +} + +// aim the specified seeker rod at the object with the key and position. +point_at_with_particles(integer which_seeker, key targetId, vector targetPosition) +{ + list paramList = [targetId, targetPosition]; + request_from_seeker(which_seeker, llDumpList2String(paramList, HUFFWARE_PARM_SEPARATOR), + HUFF_SEARCH_POINT_PARTY); +} + +// stop the particle stream running at the specified search rod. +reset_rod(integer which_seeker) { request_from_seeker(which_seeker, "", HUFF_SEARCH_RESET); } + +// support for reading configuration from notecards... + +string CARD_CONFIGURATOR_SIGNATURE = "#searchbert"; + // the notecard signature we use for our configuration. + +// tries to load a searchbert configuration notecard with the name specified. if it's blank, +// then any config notecard will do. +request_configuration(string notecard_name) +{ + if (DEBUGGING) log_it("have hit request_configuration."); + all_setup_finished = FALSE; + configuration_pending = TRUE; + // figure out whether we have a pre-chosen configuration or not. + if (llStringLength(notecard_name) == 0) { + // start reading the configuration from whatever card. + llMessageLinked(LINK_THIS, CARD_CONFIGURATOR_HUFFWARE_ID, + BEGIN_READING_NOTECARD_COMMAND, CARD_CONFIGURATOR_SIGNATURE + HUFFWARE_PARM_SEPARATOR + + wrap_item_list([])); + } else { + // get the specific one they wanted. + if (DEBUGGING) log_it("Reading configuration from notecard: " + notecard_name); + llMessageLinked(LINK_THIS, CARD_CONFIGURATOR_HUFFWARE_ID, + READ_PARTICULAR_NOTECARD_COMMAND, CARD_CONFIGURATOR_SIGNATURE + HUFFWARE_PARM_SEPARATOR + + wrap_item_list([]) + HUFFWARE_PARM_SEPARATOR + + notecard_name); + } + + // make sure we complete this in time. + llSetTimerEvent(STARTUP_TIME_ALLOWED); +} + +// this is used when we've totally failed to start up properly. +// if "arms_problem" is true, then the issue is considered to be that the seeker arms +// are missing. +complain_about_config(integer arms_problem) +{ + configuration_pending = FALSE; // it's failed, so we no longer pause for it. + string explanation = "something prevented me from putting my seeker arms back on"; + if (!arms_problem) explanation = "either no notecard had the prefix '" + + CARD_CONFIGURATOR_SIGNATURE + "'\n" + + "or this sim is very busy and timed out"; + // we hated the notecards we found, or there were none, or our arms are still ripped off. + llOwnerSay("Sorry... " + explanation + ".\nI will restart now to try again."); + llSleep(4); + llResetScript(); +} + +// handles when a menu has been clicked on. +process_menu_choice(string menu_name, string av_name, string av_key, string which_choice) +{ + if (menu_name == "main") { + if (which_choice == "Matches") { + // if there are any current matches, describe them again. + if (llGetListLength(global_matches_found)) show_off_what_was_found(); + else llSay(0, "There are no current matches."); + return; + } else if (which_choice == "Reset") { + carefully_reset_search_list(av_key); + return; + } + } +//other things will come... + + if (DEBUGGING) + log_it("non-implemented menu: " + menu_name + "/" + which_choice + " for " + av_name); +} + +// reset the search list and get ready for a new search. +carefully_reset_search_list(key id) +{ + if (check_if_search_pending(FALSE)) { + // only allow reset while running if it's the owner. + if (id != llGetOwner()) { + check_if_search_pending(TRUE); // be noisy now. + return; // we're busy. + } + llOwnerSay("Stopping active search."); + retracing_steps = TRUE; + return; + } + llSay(0, "Resetting matches."); + reset_search(); +} + +////////////// +// from hufflets... + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.8. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +integer debug_num = 0; +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llWhisper(0, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on an unusual channel for chat if it's not intended for general public. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat that anyone can hear. we take off the bling for this one. +// llSay(0, to_say); +} + +// joins a list of parameters using the parameter sentinel for the library. +string wrap_parameters(list to_flatten) +{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); } + +string wrap_item_list(list to_wrap) +{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); } + +// returns the index of the first occurrence of "pattern" inside +// the "full_string". if it is not found, then a negative number is returned. +integer find_substring(string full_string, string pattern) +{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); } + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return find_substring(compare_with, prefix) == 0; } + +// encases a list of vectors in the expected character for the jaunting library. +string wrap_vector_list(list to_wrap) +{ + integer len = llGetListLength(to_wrap); + integer i; + string to_return; + for (i = 0; i < len; i++) { + if (i > 0) to_return += "|"; + to_return += llList2String(to_wrap, i); + } + return to_return; +} + +// locates the string "text" in the list to "search_in". +integer find_in_list(list search_in, string text) +{ + integer len = llGetListLength(search_in); + integer i; + for (i = 0; i < len; i++) { + if (llList2String(search_in, i) == text) + return i; + } + return -1; +} + +integer float_precision = 2; // number of digits used when printing floats. + +//hmmm: in hufflets yet??? +string float_to_string(float to_print) +{ + string to_return = (string)to_print; + // find out where the decimal point is in the string. + integer decimal_point_posn = llSubStringIndex(to_return, "."); + if (decimal_point_posn < 0) return to_return; + return llGetSubString(to_return, 0, decimal_point_posn + float_precision); +} + +//hmmm: in hufflets yet??? +string vector_to_string(vector to_print) +{ + string to_return = "<"; + to_return += float_to_string(to_print.x); + to_return += ", "; + to_return += float_to_string(to_print.y); + to_return += ", "; + to_return += float_to_string(to_print.z); + to_return += ">"; + return to_return; +} + +// end hufflets. +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default { + state_entry() { initialize(); } + + state_exit() { llSetTimerEvent(0); } + + on_rez(integer parm) { state default; } + + timer() { handle_timer(); } + + link_message(integer which, integer num, string str, key id) + { handle_link_message(which, num, str, id); } + + changed(integer change) { + if (change & CHANGED_INVENTORY) { + llSleep(1.4); // snooze to allow other things to react first. + request_configuration(""); // see if there's a card update. + } + } + + listen(integer chan, string name, key id, string msg) { + if (msg == "#reset") { + carefully_reset_search_list(id); + } else if (is_prefix(msg, "#find ")) { + // needs arms for this trick. + if (!test_health()) state default; + // try to locate the object the user has specified. + if (check_if_search_pending(TRUE)) return; // we're busy. + reset_search(); // clean out prior state. + global_target_name = llDeleteSubString(msg, 0, 5); // Delete "#find " from msg + running_a_search = TRUE; // record that we're searching. + // point all the rods in as many different directions as we can. + skew_rod_angles(FALSE); + // look for any objects near each seeker and in the direction it's pointing. + // we double the arc we might need to try to get more matches. + start_sensing(ALL_SEEKER_ALERT, SENSOR_MAX_RANGE, + SEARCH_ROD_ANGULAR_SWEEP, global_target_name, MAX_MATCHES); + // start stepping through our search spirals. + llSetTimerEvent(FAST_TIMER_INTERVAL); + llSay(0, "Searching for " + global_target_name); + } else if (is_prefix(msg, "#initialize")) { + llResetScript(); + } else if (is_prefix(msg, "#skew")) { + llSetTimerEvent(SNOOZE_BEFORE_RESET); + reset_for_next_timer = TRUE; + skew_rod_angles(TRUE); + } else if (is_prefix(msg, "#channel")) { + HUFF_SEARCH_CHAT_CHANNEL = (integer)llDeleteSubString(msg, 0, 7); + llSay(0, "Changed listening channel to " + (string)HUFF_SEARCH_CHAT_CHANNEL + "."); + llMessageLinked(LINK_THIS, SEARCHBERT_MENUS_HUFFWARE_ID, SM_CONFIGURE_INFO, + wrap_parameters([HUFF_SEARCH_CHAT_CHANNEL])); + apply_variable_definition("huff_search_chat_channel", (string)HUFF_SEARCH_CHAT_CHANNEL); + } + } + + touch_start(integer num) { + // make sure we have already been set up. + if (!test_health()) state default; + + if (check_if_search_pending(TRUE)) return; + + // send a request to the menu script for the users' clicks... + integer indy; + for (indy = 0; indy < num; indy++) { + // request new menu popup for each av that clicked. + llMessageLinked(LINK_THIS, SEARCHBERT_MENUS_HUFFWARE_ID, SM_POP_MAIN_MENU_UP, + wrap_parameters([llDetectedName(indy), llDetectedKey(indy)])); + } + } +} + +///////// +// original attributions: +// Special Particle Sensor "Brain" Script +// Written by Christopher Omega +// Tasks: Listen to the owner, Parse the owner's message, Signal individual locators +// to reset, Or point at a certain object within 96 meters of the apparatus. +///////// + +// note by fred huffhines: +// much of this script was originally written by the attributed authors above. however, +// i have spent a ton of time improving this pair of scripts... (the brain and the +// search pointer). i've added pattern matching (rather than needing to know exact +// names), added the spiral search traversal method to improve search behavior and +// get more results, improved the particle streams, and other bits. i just recently added +// configurability from notecards for searchbert options and a menuing system to provide +// help and runtime control over some of the options. wheeee, it's been a lot of fun. +// --fred. diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/huff-search_pointer_v10.0.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/huff-search_pointer_v10.0.lsl new file mode 100755 index 00000000..8cdeb3f4 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/huff-search_pointer_v10.0.lsl @@ -0,0 +1,574 @@ + +// huffware script: huff-search pointer, by fred huffhines +// +// note: parts of this script were written by the attributed authors below. +// +// this script is one portion of a search system. the pointer is meant to be in a child prim. +// it is told what to point at by the root prim. +// +// the newer version of this supports a search command that locates objects in the +// direction specified. +// +// original attributions: started life as "Particle Script 0.4, Created by Ama Omega, 3-7-2004" +// some code in this script is also from: Christopher Omega. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// huff-search pointer API: +////////////// +// do not redefine these constants. +integer HUFF_SEARCH_POINTER_HUFFWARE_ID = 10032; + // the unique id within the huffware system for this script. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +////////////// +string HUFF_SEARCH_RESET = "#reset#"; + // returns the script to its starting state. +string HUFF_SEARCH_POINT_PARTY = "#point_particles#"; + // aim at an object and show a particle stream leading to it. +string HUFF_SEARCH_JUST_POINT = "#just_point#"; + // aim at an object, but don't do any particles. +string HUFF_SEARCH_SENSEI = "#sensor#"; + // set up a sensor request for a search pattern. pings will cause + // the pattern to be sought in names of nearby objects. the parameters are: + // (1) the maximum range for the sensor, (2) the arc angle to use in sensing, + // (3) the search pattern to look for in object names, (4) the maximum number + // of matches to look for. +string HUFF_SEARCH_STOP_SENSE = "#stop_sensor#"; + // turn off the sensor but don't totally reset. +string HUFF_SEARCH_PING = "#ping#"; + // cause the searcher to actively sensor ping the targets. +string HUFF_SEARCH_MATCH_EVENT = "#match#"; + // fired at the root prim when matches are found for the search term. + // the payload is a list of matched item pairs [item key, location]. +////////////// + +// global variables... + +// SENSOR_TYPE_ALL: a constant that tells the sensor to look for (ACTIVE|PASSIVE|AGENT). +integer SENSOR_TYPE_ALL = 7; + +// AXIS_* constants, represent the unit vector 1 unit on the specified axis. +vector AXIS_UP = <0.0, 0.0, 1.0>; +vector AXIS_LEFT = <0.0, 1.0, 0.0>; +vector AXIS_FWD = <1.0, 0.0, 0.0>; + +integer MAX_LIST_LEN = 17; + // the maximum matches we hang onto, to avoid using too many resources. + // this should be no longer than the number of search arms created by + // the brain script, but it can be less if fewer matches are needed. + +/////////////// + +// global variables... + +string search_pattern; + // the pattern that we are hoping to find from our sensor hits. + +list global_matches_found; + // a list of keys that match the specified search terms. + +integer our_link_number = 0; + // set to the number for our link if we ever see ourselves as a link greater than 1. + // this only happens when we have been made a sub-prim and are definitely not the root prim, + // so it is our key for whether the prim should die if it becomes unlinked. + +// Mask Flags - set to TRUE to enable +integer glow = TRUE; // Make the particles glow +integer bounce = FALSE; // Make particles bounce on Z plane of object +integer interpColor = TRUE; // Go from start to end color +integer interpSize = TRUE; // Go from start to end size +integer wind = FALSE; // Particles effected by wind +integer followSource = TRUE; // Particles follow the source +integer followVel = TRUE; // Particles turn to velocity direction + +// Choose a pattern from the following: +// PSYS_SRC_PATTERN_EXPLODE +// PSYS_SRC_PATTERN_DROP +// PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY +// PSYS_SRC_PATTERN_ANGLE_CONE +// PSYS_SRC_PATTERN_ANGLE +integer pattern = PSYS_SRC_PATTERN_DROP; + +// Select a target for particles to go towards +// "" for no target, "owner" will follow object owner +// and "self" will target this object +// or put the key of an object for particles to go to +key target = "owner"; + +// useful particle parameters. +float age = 14; // Life of each particle +float startAlpha = 1.0; // Start alpha (transparency) value +float endAlpha = 1.0; // End alpha (transparency) value +vector startSize = <0.2, 0.2, 0.2>; // Start size of particles +vector endSize = <0.8, 0.8, 0.8>; // End size of particles (if interpSize == TRUE) + +// colors are now assigned dynamically per search. +vector startColor; // Start color of particles +vector endColor; // End color of particles (if interpColor == TRUE) + +// unused particle parameters. +float maxSpeed = 2; // Max speed each particle is spit out at +float minSpeed = 2; // Min speed each particle is spit out at +string texture; // Texture used for particles, default used if blank +vector push = <0.0, 0.0, 0.0>; // Force pushed on particles + +// System parameters +float rate = 0.08; // burst rate to emit particles, zero is fastest. +float radius = 1; // Radius to emit particles for BURST pattern +integer count = 1; // How many particles to emit per BURST +float outerAngle = 1.54; // Outer angle for all ANGLE patterns +float innerAngle = 1.55; // Inner angle for all ANGLE patterns +vector omega = <0,0,0>; // Rotation of ANGLE patterns around the source +float life = 0; // Life in seconds for the system to make particles + +// Script variables +integer precision = 2; //Adjust the precision of the generated list. + +integer running_particles = FALSE; // is the particle system running? + +///integer start_parm; // set from the on_rez parameter. + +string float2String(float in) +{ + return llGetSubString((string)in, 0, precision - 7); +} + +create_particles() +{ + list system_content; + integer flags = 0; + if (target == "owner") target = llGetOwner(); + if (target == "self") target = llGetKey(); + if (glow) flags = flags | PSYS_PART_EMISSIVE_MASK; + if (bounce) flags = flags | PSYS_PART_BOUNCE_MASK; + if (interpColor) flags = flags | PSYS_PART_INTERP_COLOR_MASK; + if (interpSize) flags = flags | PSYS_PART_INTERP_SCALE_MASK; + if (wind) flags = flags | PSYS_PART_WIND_MASK; + if (followSource) flags = flags | PSYS_PART_FOLLOW_SRC_MASK; + if (followVel) flags = flags | PSYS_PART_FOLLOW_VELOCITY_MASK; + if (target != "") flags = flags | PSYS_PART_TARGET_POS_MASK; + + // original recipe searchbert pointer... +// startColor = <0.92, 0.79412, 0.66863>; // Start color of particles +// endColor = <0.0, 1.0, 0.7>; // End color of particles (if interpColor == TRUE) + // new randomized version for colors. the current aesthetic here is to start + // with a relatively dark color and end on a relatively light color. + startColor = ; + endColor = ; + + system_content = [ PSYS_PART_MAX_AGE, age, + PSYS_PART_FLAGS,flags, + PSYS_PART_START_COLOR, startColor, + PSYS_PART_END_COLOR, endColor, + PSYS_PART_START_SCALE, startSize, + PSYS_PART_END_SCALE, endSize, + PSYS_SRC_PATTERN, pattern, + PSYS_SRC_BURST_RATE, rate, + PSYS_SRC_ACCEL, push, + PSYS_SRC_BURST_PART_COUNT, count, + PSYS_SRC_BURST_RADIUS, radius, + PSYS_SRC_BURST_SPEED_MIN, minSpeed, + PSYS_SRC_BURST_SPEED_MAX, maxSpeed, +// PSYS_SRC_INNERANGLE,innerAngle, +// PSYS_SRC_OUTERANGLE,outerAngle, + PSYS_SRC_OMEGA, omega, + PSYS_SRC_MAX_AGE, life, +// PSYS_SRC_TEXTURE, texture, + PSYS_PART_START_ALPHA, startAlpha, + PSYS_PART_END_ALPHA, endAlpha + ]; + if (target != NULL_KEY) { + system_content += [ PSYS_SRC_TARGET_KEY, target ]; + } + + llParticleSystem(system_content); + running_particles = TRUE; +} + +// returns TRUE if the value in "to_check" specifies a legal x or y value in a sim. +integer valid_sim_value(float to_check) +{ + if (to_check < 0.0) return FALSE; + if (to_check >= 257.0) return FALSE; + return TRUE; +} + +integer outside_of_sim(vector to_check) +{ + return !valid_sim_value(to_check.x) || !valid_sim_value(to_check.y); +} + +/////////////// + +// SetLocalRot +// In a linked set, points a child object to the rotation. +// @param rot The rotation to rotate to. +SetLocalRot(rotation rot) +{ + if(llGetLinkNumber() > 1) + { + rotation locRot = llGetLocalRot(); + locRot.s = -locRot.s; // Invert local rot. + + rotation parentRot = locRot * llGetRot(); + parentRot.s = -parentRot.s; // Invert parent's rot. + + llSetLocalRot(rot * parentRot); + } +} + +// Gets the rotation to point the specified axis at the specified position. +// @param axis The axis to point. Easiest to just use an AXIS_* constant. +// @param target The target, in region-local coordinates, to point the axis at. +// @return The rotation necessary to point axis at target. +rotation getRotToPointAxisAt(vector axis, vector target) +{ + return llGetRot() * llRotBetween(axis * llGetRot(), target - llGetPos()); +} + +// aims in the direction of the target. +aim_at(vector targetPos) +{ + SetLocalRot(getRotToPointAxisAt(AXIS_UP, targetPos)); +} + +// locates the string "text" in the list to "search_in". +integer find_in_list(list search_in, string text) +{ + integer len = llGetListLength(search_in); + integer i; + for (i = 0; i < len; i++) { + if (llList2String(search_in, i) == text) + return i; + } + return -1; +} + +// point_at +// Points up axis at targetPos, and emits a particle system at targetKey. +// @param targetKey The UUID of the target to emit particles to. +// @param targetPos The poaition of the target in region-local coordinates. +point_at(key targetKey, vector targetPos) +{ + aim_at(targetPos); + target = targetKey; + create_particles(); +} + +// variables that are established by a search and used periodically in the timer. +float max_range = 0.0; +float arc_angle = 0.0; +float sensor_interval = 0.0; + +reset_sensors() +{ + radius = 1; + llSetTimerEvent(0.0); + llSensorRemove(); +} + +////////////// +// from hufflets... + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on an unusual channel for chat if it's not intended for general public. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat that anyone can hear. we take off the bling for this one. +// llSay(0, to_say); +} + +// returns TRUE if the "pattern" is found in the "full_string". +integer matches_substring(string full_string, string pattern) +{ return (find_substring(full_string, pattern) >= 0); } + +// returns the index of the first occurrence of "pattern" inside +// the "full_string". if it is not found, then a negative number is returned. +integer find_substring(string full_string, string pattern) +{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); } + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return find_substring(compare_with, prefix) == 0; } + +// returns a number at most maximum and at least minimum. +// if "allow_negative" is TRUE, then the return may be positive or negative. +float randomize_within_range(float minimum, float maximum, integer allow_negative) +{ + float to_return = minimum + llFrand(maximum - minimum); + if (allow_negative) { + if (llFrand(1.0) < 0.5) to_return *= -1.0; + } + return to_return; +} + +// makes sure that we record the current link number if it's higher than 1; this +// is how we know that we're a sub-prim. +record_link_num_if_useful() +{ + if (llGetLinkNumber() > 1) our_link_number = llGetLinkNumber(); +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + llParticleSystem([]); + running_particles = FALSE; + SetLocalRot(<0.0, 0.0, 0.0, 1.0>); + global_matches_found = []; + record_link_num_if_useful(); + } + on_rez(integer parm) { + record_link_num_if_useful(); + state default; + } + link_message(integer sender, integer num, string command, key parameters) { + if (num != HUFF_SEARCH_POINTER_HUFFWARE_ID) return; // not for us. + if (llGetLinkNumber() <= 1) return; // do nothing as root prim. + if (command == HUFF_SEARCH_RESET) { + // returns to the normal state of the object. + reset_sensors(); + SetLocalRot(<0, 0, 0, 1>); + llParticleSystem([]); + running_particles = FALSE; + global_matches_found = []; + } else if (command == HUFF_SEARCH_POINT_PARTY) { + // aim at an object and show a particle stream leading to it. + reset_sensors(); + list parsedParameters = llParseString2List(parameters, [HUFFWARE_PARM_SEPARATOR], []); + key targetKey = (key)llList2String(parsedParameters, 0); + vector targetPos = (vector)llList2String(parsedParameters, 1); + point_at(targetKey, targetPos); + } else if (command == HUFF_SEARCH_JUST_POINT) { + // aim at an object, but don't do any particles. + reset_sensors(); + list parsedParameters = llParseString2List(parameters, [HUFFWARE_PARM_SEPARATOR], []); + vector targetPos = (vector)llList2String(parsedParameters, 0); + aim_at(targetPos); + } else if (command == HUFF_SEARCH_SENSEI) { + // set up a sensor request for a search pattern. pings will cause + // the pattern to be sought in names of nearby objects. + reset_sensors(); + global_matches_found = []; // reset any previous matches. + list parsedParameters = llParseString2List(parameters, [HUFFWARE_PARM_SEPARATOR], []); + max_range = (float)llList2String(parsedParameters, 0); + arc_angle = (float)llList2String(parsedParameters, 1); + search_pattern = llToLower((string)llList2String(parsedParameters, 2)); + MAX_LIST_LEN = (integer)llList2String(parsedParameters, 3); +if (!MAX_LIST_LEN) { +MAX_LIST_LEN = 17; +log_it("failed to get list length param"); +} + } else if (command == HUFF_SEARCH_STOP_SENSE) { + // turn off the sensor but don't totally reset yet. + reset_sensors(); + global_matches_found = []; + } else if (command == HUFF_SEARCH_PING) { + // do a little particle emission while searching, just to let them know + // where we've been. + target = "self"; + radius = 5; + if (!running_particles) create_particles(); + // they want to check for objects right here, right now... + llSensor("", NULL_KEY, SENSOR_TYPE_ALL, max_range, arc_angle); + } + } + changed(integer change) { + if (change & CHANGED_LINK) { + // we have been linked or unlinked or sat upon. + if ( (our_link_number > 1) && (llGetLinkNumber() == 0) ) { + // this is now a single prim linked to nothing. + llDie(); + } else if (our_link_number <= 1) { + // we had no link number recorded, so let's track this if needed. + record_link_num_if_useful(); + } + } + } + sensor(integer num_detected) { + if (llGetLinkNumber() <= 1) return; // do nothing as root prim. + if (llGetListLength(global_matches_found) > MAX_LIST_LEN) { + // we have enough matches already. stop adding more. + return; + } + + list parms = []; // the full set of matches we found. + integer i; // loop variable. + integer matches_found = 0; + key root_key = llGetLinkKey(1); + for (i = 0; i < num_detected; i++) { + key targetKey = llDetectedKey(i); + string target_name = llDetectedName(i); + if ( (targetKey != root_key) + // we don't want to report our own object. + && matches_substring(target_name, search_pattern) + // check whether the current target matches our search pattern. + && (find_in_list(global_matches_found, targetKey) < 0) ) { + // make sure we haven't already reported this one. + +//if (matches_substring(target_name, "searchbert v")) { +//log_it("somehow got past the is it myself check with a no-answer, my key=" + llGetKey() + " their key=" + targetKey); +//} + + // store the match now. even if we don't like where it's located + // (as in, outside our sim), we still don't want to keep matching it + // and looking at it. + global_matches_found += targetKey; + + // the name matched the search pattern... but make sure the + // location is worth reporting; if it's outside the sim, we will + // not be able to name it or point at it properly. + vector location = llDetectedPos(i); + if (!outside_of_sim(location)) { + // it's a match that's inside the sim. send it along. + parms += [ targetKey, location ]; + matches_found++; // we got one! +//log_it("match added: " + (string)targetKey); + // shorten lists of matches so we don't overload the brain. + if (llGetListLength(parms) >= 8) { + llMessageLinked(LINK_ROOT, + HUFF_SEARCH_POINTER_HUFFWARE_ID + REPLY_DISTANCE, + HUFF_SEARCH_MATCH_EVENT, + llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR)); + matches_found = 0; + parms = []; + } + } + } + } + if (matches_found) { +//log_it("sending " + (string)matches_found + " matches in message..."); + // send message about matches back to parent. + llMessageLinked(LINK_ROOT, HUFF_SEARCH_POINTER_HUFFWARE_ID + REPLY_DISTANCE, + HUFF_SEARCH_MATCH_EVENT, + llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR)); + } + } +} diff --git a/huffware/huffotronic_updater_freebie_v5.3/huff-update_client_v20.1.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/huff-update_client_v20.1.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/huff-update_client_v20.1.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/huff-update_client_v20.1.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/huffbee_bulb_v4.7.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/huffbee_bulb_v4.7.lsl new file mode 100755 index 00000000..8f06aa13 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/huffbee_bulb_v4.7.lsl @@ -0,0 +1,252 @@ + +// huffware script: "huffbee bulb", by fred huffhines +// +// inspired by the higbee bulb script available in open source, but provides a few +// different functions. this accepts a click to turn on and off, but it also listens +// to a special command to hear requests from linked objects. this also shuts off +// at dusk and turns on at dawn. +// this script uses the party culiar script to get the particle system created, +// and hence relies on a notecard to define the lamp's particles. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +integer DUSK_CHECKING_PERIOD = 240; + // check for dusk this frequently. in seconds, so 240 = 4 minutes. + +integer SECRET_LIGHT_BULB_ILLUMINATI_CODE = -14058; + // a semi-secret code that is used in a linked message when some other script + // wants the lamp to turn on or off. + +float BULB_GLOW_LEVEL = 0.5; // how much glow should the bulb have when it's lit? + +integer CHECK_FOR_DUSK = TRUE; // true if this lamp should be driven by daylight. + +integer PARTICLE_FIX_COG = 7; // every N timer hits, we'll fix our particles. + +// party culiar link message API. +////////////// +integer PARTYCULIAR_HUFFWARE_ID = 10018; + // a unique ID within the huffware system for this script. +string HUFFWARE_PARM_SEPARATOR = "~~~"; + // three tildes is an uncommon thing to have otherwise, so we use it to separate + // our commands in linked messages. +// +string PARTYCULIAR_POWER_COMMAND = "#powrpcl"; + // tells the particle system to either turn on or off. the single parameter is + // either "on", "1", "true", or "off", "0", "false". +////////////// + +// globals. + +integer particle_cog = 0; + +////////////// + +// asks the particle system script for a favor, given by the command and the +// parameters. +send_partyculiar_request(string cmd, string params) +{ llMessageLinked(LINK_THIS, PARTYCULIAR_HUFFWARE_ID, cmd, params); } + +// shuts down the lamp. there is no coming back from this method, +// since it resets the script. +turn_off_lamp() +{ + turn_off_particles(); + llResetScript(); +} + +turn_off_particles() { send_partyculiar_request(PARTYCULIAR_POWER_COMMAND, "off"); } + +turn_on_particles() { send_partyculiar_request(PARTYCULIAR_POWER_COMMAND, "on"); } + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { + auto_retire(); + turn_off_particles(); + // reset the bulb to a fairly dark color with no glow and no light. + llSetPrimitiveParams([ + PRIM_COLOR, ALL_SIDES, <0.42, 0.42, 0.42>, 0.5, + PRIM_FULLBRIGHT, ALL_SIDES, FALSE, + PRIM_GLOW, ALL_SIDES, 0.0, + PRIM_POINT_LIGHT, FALSE, <0, 0, 0>, 1.0, 10.0, 0.25 + ]); + llSetTimerEvent(DUSK_CHECKING_PERIOD); + } + on_rez(integer parm) { + state default; + } + timer() { + vector sun = llGetSunDirection(); + if (CHECK_FOR_DUSK && (sun.z <= 0) ) + state PowerUp; // turn on at dusk. + } + touch_start(integer total_number) { + state PowerUp; + } + + // handle requests to ignite the lamp. + link_message(integer sender, integer num, string str, key id) { + // we only support one message really, which is to turn on/off. + if (num == SECRET_LIGHT_BULB_ILLUMINATI_CODE) { + state PowerUp; // we know we aren't lit yet, so get that way. + } + } +} + +state PowerUp +{ + state_entry() { + // light the bulb, with a full color / full bright assault on + // the sense, using real light and a glow effect. + llSetPrimitiveParams([ + PRIM_COLOR, ALL_SIDES, <1, 1, 1>, 0.5, + PRIM_FULLBRIGHT, ALL_SIDES, TRUE, + PRIM_GLOW, ALL_SIDES, BULB_GLOW_LEVEL, + PRIM_POINT_LIGHT, TRUE, <.8, .8, .3>, 1.0, 20.0, .9 + ]); + turn_on_particles(); + llSetTimerEvent(DUSK_CHECKING_PERIOD); + } + on_rez(integer parm) { + // if we rezzed in this state, that's a mistake. the lamp is intended + // to start in a dark state. + turn_off_lamp(); + } + timer() { + particle_cog++; + if (particle_cog >= PARTICLE_FIX_COG) { + // periodically refresh the particle system. + turn_on_particles(); + particle_cog = 0; // reset cog count. + } + // also periodically check if we should shut off. + vector sun = llGetSunDirection(); + if (CHECK_FOR_DUSK && (sun.z > 0) ) { + // turn off at dawn. + turn_off_lamp(); + } + } + + touch_start(integer total_number) + { + turn_off_lamp(); + } + + link_message(integer sender, integer num, string str, key id) { + // we only support one message really, which is to turn on/off. + if (num == SECRET_LIGHT_BULB_ILLUMINATI_CODE) { + turn_off_lamp(); // in this state, we are already lit, so get real dark. + } + } +} diff --git a/huffware/huffotronic_updater_freebie_v5.3/hufflets_v6.3.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/hufflets_v6.3.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/hufflets_v6.3.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/hufflets_v6.3.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/huffware_id_registry_v3.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/huffware_id_registry_v3.2.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/huffware_id_registry_v3.2.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/huffware_id_registry_v3.2.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/inventory_exchanger_v3.7.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/inventory_exchanger_v3.7.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/inventory_exchanger_v3.7.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/inventory_exchanger_v3.7.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/invoke_nechung_v1.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/invoke_nechung_v1.2.lsl new file mode 100755 index 00000000..2ae168d9 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/invoke_nechung_v1.2.lsl @@ -0,0 +1,135 @@ + +// huffware script: invoke nechung, by fred huffhines. +// +// an example of invoking a web site from SL and getting the response. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +key last_request_id; // the current request that's in progress, if any. + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { + auto_retire(); + } + + touch_start(integer num_touched) { + last_request_id = llHTTPRequest("http://gruntose.com//cgi-bin/nechung.cgi", [HTTP_METHOD,"GET"], ""); + } + + http_response(key request_id, integer status, list metadata, string body) + { + llSay(0, "\n" + body); + } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/jaunting_library_v15.9.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/jaunting_library_v15.9.lsl new file mode 100755 index 00000000..4d65cee3 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/jaunting_library_v15.9.lsl @@ -0,0 +1,551 @@ + +// huffware script: jaunting library, by fred huffhines, released under GPL-v3 license. +// +// this script is a library of useful teleportation and movement calls. it should be added +// into an object along with other scripts that use the library. the jaunting library +// responds to linked messages as commands. +// parts of this script are based on warpPos from "Teleporter Script v 3.0 by Asira Sakai", +// which was found at the LSL wiki. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// global constants... + +float PROXIMITY_REQUIRED = 0.1; + // how close we must be to the target location to call the jaunt done. + // we make this fairly low accuracy since we don't want jumps in space to immediately + // be counted as failures. + +// the most jumps the script will try to take. the overall distance from the start +// to the end can be 10 * MAXIMUM_JUMPS_PER_CALL meters. +integer MAXIMUM_JUMPS_PER_CALL = 84; + +// global variables... + +vector last_posn; // used to calculate jump distances. + +// jaunting library link message API... +////////////// +// do not redefine these constants. +integer JAUNT_HUFFWARE_ID = 10008; + // the unique id within the huffware system for the jaunt script to + // accept commands on. this is used in llMessageLinked as the num parameter. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +// commands available via the jaunting library: +string JAUNT_COMMAND = "#jaunt#"; + // command used to tell jaunt script to move object. pass a vector with the location. +string FULL_STOP_COMMAND = "#fullstop#"; + // command used to bring object to a halt. +string REVERSE_VELOCITY_COMMAND = "#reverse#"; + // makes the object reverse its velocity and travel back from whence it came. +string SET_VELOCITY_COMMAND = "#setvelocity#"; + // makes the velocity equal to the vector passed as the first parameter. +string JAUNT_UP_COMMAND = "#jauntup#"; +string JAUNT_DOWN_COMMAND = "#jauntdown#"; + // commands for height adjustment. pass a float for number of meters to move. +string JAUNT_LIST_COMMAND = "#jauntlist#"; + // like regular jaunt, but expects a list of vectors as the first parameter; this list + // should be in the jaunter notecard format (separated by pipe characters). + // the second parameter, if any, should be 1 for forwards traversal and 0 for backwards. +////////////// + +// returns what we consider to be safe to do in one big jump. +float distance_safe_to_jaunt() { return (float)(MAXIMUM_JUMPS_PER_CALL - 4) * 10.0; } + +// tests whether the destination is safe for an object to enter. +integer safe_for_entry(vector where) +{ + if (outside_of_sim(where)) { + // that's an obvious wrong idea; it tends to break us. + return FALSE; + } + return TRUE; +} + +// helper function for warp_across_list. this adds one jump vector to the +// list of rules as long as the destination is interesting. +list process_warp_item(vector next_jump) +{ +//log_it("mem: " + (string)llGetFreeMemory()); + // calculate the number of jumps needed. + integer jumps = (integer)(llVecDist(next_jump, last_posn) / 10.0) + 1; + last_posn = next_jump; // record for next check. + // set up our list which we'll replicate. + list rules = [ PRIM_POSITION, next_jump ]; + // Try and avoid stack/heap collisions. + if (jumps > MAXIMUM_JUMPS_PER_CALL - 1) jumps = MAXIMUM_JUMPS_PER_CALL; + // add the rules repeatedly to get the effective overall jump done. + integer count = 1; + while ( (count = count << 1) < jumps) + rules += rules; + // magnify the rule list before really adding it. this gets us to the proper + // final number of jumps. + return rules + llList2List(rules, (count - jumps) << 1 - 2, count); +} + +// originally based on warpPos from lsl wiki but drastically modified. +// uses a set of transfer points instead of a single target. +list warp_across_list(list full_journey, integer forwards) +{ + // make sure the list didn't run out. + if (llGetListLength(full_journey) == 0) return []; + if (forwards) { + // forwards traversal of the list. + vector next_jump = (vector)llList2String(full_journey, 0); + // shortcut the jumps if we're already there. + if (next_jump == llGetPos()) + return warp_across_list(chop_list(full_journey, 1, + llGetListLength(full_journey) - 1), forwards); + // calculate our trajectory for the next jump and add in all subsequent jumps. + return process_warp_item(next_jump) + + warp_across_list(chop_list(full_journey, 1, llGetListLength(full_journey) - 1), forwards); + } else { + // reverse traversal of the list. + vector next_jump = (vector)llList2String(full_journey, llGetListLength(full_journey) - 1); + // shortcut the jumps if we're already there. + if (next_jump == llGetPos()) + return warp_across_list(chop_list(full_journey, 0, + llGetListLength(full_journey) - 2), forwards); + // calculate our trajectory for the next jump and add in all subsequent jumps. + return process_warp_item(next_jump) + + warp_across_list(chop_list(full_journey, 0, llGetListLength(full_journey) - 2), forwards); + } +} + +// the main function that performs the jaunting process. +// now also supports adding a rotation into the mix to avoid paying the extra delay +// cost of calling llSetRot. +jaunt(list full_journey, integer forwards, string command_used, + integer use_rotation, rotation rot, integer reply_to_request) +{ +//log_it("jaunt: fullj=" + (string)full_journey + " forew=" + (string)forwards); + // set up our global variables... + last_posn = llGetPos(); + // calculate the trip and run it. + list rot_add; + if (use_rotation) + rot_add = [ PRIM_ROTATION, rot ]; + llSetPrimitiveParams(warp_across_list(full_journey, forwards) + rot_add); + if (reply_to_request) { + // pick out the last target in the list based on the direction we're moving. + integer last_indy = 0; + if (forwards == TRUE) last_indy = llGetListLength(full_journey) - 1; + vector last_jump = (vector)llList2String(full_journey, last_indy); + // final judge of success here is how close we got to the target. + integer landed_there = (llVecDist(llGetPos(), last_jump) <= PROXIMITY_REQUIRED); + send_reply(LINK_THIS, [landed_there, llGetPos()], command_used); + } +} + +////////////// + +// sets the object's speed to "new_velocity". +// if "local_axis" is TRUE, then it will be relative to the object's +// own local coordinates. +set_velocity(vector new_velocity, integer local_axis) +{ + vector current_velocity = llGetVel(); + + if (local_axis) { + rotation rot = llGetRot(); + current_velocity /= rot; // undo the rotation. + } + + new_velocity -= current_velocity; + new_velocity *= llGetMass(); + + llApplyImpulse(new_velocity, local_axis); +} + +// attempts to bring the object to a complete stop. +full_stop() +{ + llSetForce(<0,0,0>, FALSE); + set_velocity(<0,0,0>, FALSE); +} + +// sends an object back along its trajectory. +reverse_velocity(integer local_axis) +{ + vector current_velocity = llGetVel(); + if (local_axis) { + rotation rot = llGetRot(); + current_velocity /= rot; // undo the rotation. + } + vector new_velocity = -2 * current_velocity; + new_velocity *= llGetMass(); + llApplyImpulse(new_velocity, local_axis); +} + +// teleports to the new "location", if possible. does not change object's phantom / physical +// states. this will do the jaunt in multiple steps if the distance from here to "location" +// is too large. +apportioned_jaunt(vector location, string command_used, integer use_rotation, rotation rot, + integer should_send_reply) +{ + if (!safe_for_entry(location)) { + // that's not good. we should not allow the script to get broken. + if (should_send_reply) + send_reply(LINK_THIS, [FALSE, llGetPos()], command_used); + return; + } + // go to position specified, by leapfrogs if too long. + integer chunk_of_jump; + integer MAX_CHUNKS = 200; // multiplies the distance a single jaunt can cover. + for (chunk_of_jump = 0; chunk_of_jump < MAX_CHUNKS; chunk_of_jump++) { + vector interim_vec = location - llGetPos(); + float jump_dist = llVecDist(llGetPos(), location); + integer reply_needed = TRUE; + if (jump_dist > distance_safe_to_jaunt()) { + // the entire distance cannot be jumped. do the part of it that fits. + float proportion_can_do = distance_safe_to_jaunt() / jump_dist; + interim_vec *= proportion_can_do; + reply_needed = FALSE; // don't reply when this is not full jump. + } + interim_vec += llGetPos(); // bias jump back to where we are. +//log_it("jumping from " + (string)llGetPos() + " to " + (string)interim_vec); + jaunt([llGetPos(), interim_vec], TRUE, command_used, use_rotation, rot, + reply_needed && should_send_reply); + float dist_now = llVecDist(llGetPos(), interim_vec); + if (dist_now > PROXIMITY_REQUIRED) { +//log_it("failed to make interim jump, dist left is " + (string)dist_now); + // bail out. we failed to get as far as we thought we should. + chunk_of_jump = MAX_CHUNKS + 10; + if (!reply_needed) { + // we need to send the reply we hadn't sent yet. + if (should_send_reply) + send_reply(LINK_THIS, [FALSE, llGetPos()], command_used); + } + } else if (llVecDist(llGetPos(), location) <= PROXIMITY_REQUIRED) { + // leave loop for a different reason; we got there. + chunk_of_jump = MAX_CHUNKS + 10; + if (!reply_needed) { + // we need to send the reply we hadn't sent yet. + if (should_send_reply) + send_reply(LINK_THIS, [TRUE, llGetPos()], command_used); + } + } + } +} + +// the entire distance embodied in the list of targets. this is a little smart, +// in that if there's only one target, we assume we're going from "here" to that +// one target. +float total_jaunt_distance(list targets) +{ + if (!llGetListLength(targets)) return 0.0; + // add in "this" location if they omitted it. + if (llGetListLength(targets) < 2) + targets = [ llGetPos() ] + targets; + integer disindy; + float total_dist = 0.0; + vector prev = (vector)llList2String(targets, 0); + for (disindy = 1; disindy < llGetListLength(targets); disindy++) { + vector next = (vector)llList2String(targets, disindy); + total_dist += llVecDist(prev, next); + prev = next; + } + return total_dist; +} + +// jaunts to a target via a set of intermediate locations. can either go forwards +// through the list or backwards. +phantom_jaunt_to_list(list targets, integer forwards, string command_used, + integer use_rotation, rotation rot) +{ + vector final_loc; + if (forwards) final_loc = (vector)llList2String(targets, llGetListLength(targets) - 1); + else final_loc = (vector)llList2String(targets, 0); + +//hmmm: check each destination in list?? + if (!safe_for_entry(final_loc)) { + // that's not good. we should not allow the script to get broken. + send_reply(LINK_THIS, [FALSE, llGetPos()], command_used); + return; + } + + // this turns off the physics property for the object, so that jaunt and + // llSetPos will still work. this became necessary due to havok4. + integer original_phantomosity = llGetStatus(STATUS_PHANTOM); + integer original_physicality = llGetStatus(STATUS_PHYSICS); + if (original_physicality != FALSE) llSetStatus(STATUS_PHYSICS, FALSE); + if (original_phantomosity != TRUE) llSetStatus(STATUS_PHANTOM, TRUE); + + integer send_reply_still = TRUE; // true if we should send a reply when done. + + // iterate through our list of targets and either we will jaunt to the next + // place directly or we will have to wrap a few destinations due to sim crossing. + while (llGetListLength(targets) > 0) { + vector next_loc; + if (forwards) next_loc = (vector)llList2String(targets, 0); + else next_loc = (vector)llList2String(targets, llGetListLength(targets) - 1); + if (outside_of_sim(next_loc)) { + log_it("bad jaunt path: first target is out of sim."); + send_reply(LINK_THIS, [FALSE, llGetPos()], command_used); + return; // skip that badness. + } + + // check how much total distance we have in the path that's left. if it's under our + // limit, then we'll just take it in one jump. + float total_dist = total_jaunt_distance(targets); + + // if we're below the threshold, we'll just jump now. + integer already_jumped = FALSE; + if (total_dist < distance_safe_to_jaunt()) { + jaunt(targets, forwards, command_used, use_rotation, rot, TRUE); + targets = []; // reset the list now. + send_reply_still = FALSE; // we have already sent the reply in jaunt(). + already_jumped = TRUE; // don't do anything else. + } + if (!already_jumped) { + vector next_plus_1 = ZERO_VECTOR; // default cannot fail our "is inside sim" check. + if (llGetListLength(targets) > 1) { + if (forwards) next_plus_1 = (vector)llList2String(targets, 1); + else next_plus_1 = (vector)llList2String(targets, llGetListLength(targets) - 2); + } + if (outside_of_sim(next_plus_1)) { +//hmmm: eventually find all the negative ones in a row and do them in combo, rather than +// just giving up here and doing a jaunt to the rest.. + jaunt(targets, forwards, command_used, use_rotation, rot, TRUE); + targets = []; // reset the list now. + send_reply_still = FALSE; // we have already sent the reply in jaunt(). + } else { + // we've passed the negativity test, so we can at least jump to the next place. + + // zap the next location, since we're about to jump there. + integer zap_pos = 0; + if (!forwards) zap_pos = llGetListLength(targets) - 1; + targets = llDeleteSubList(targets, zap_pos, zap_pos); + + // only bother jumping if we're not already there. + if (llVecDist(next_loc, llGetPos()) > PROXIMITY_REQUIRED) { + apportioned_jaunt(next_loc, command_used, use_rotation, rot, FALSE); + } + } + } + } + + if (send_reply_still) { + integer yippee = TRUE; // assume we succeeded until we prove otherwise. + if (llVecDist(final_loc, llGetPos()) > PROXIMITY_REQUIRED) + yippee = FALSE; + send_reply(LINK_THIS, [yippee, llGetPos()], command_used); + } + + // return to prior characteristics. + if (original_phantomosity != TRUE) llSetStatus(STATUS_PHANTOM, original_phantomosity); + if (original_physicality != FALSE) llSetStatus(STATUS_PHYSICS, original_physicality); +} + +// implements our API for jumping around. +handle_link_message(integer sender, integer huff_id, string msg, key id) +{ + // separate the list out + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + if (msg == JAUNT_COMMAND) { + // use list to string to avoid non-conversions. + vector v = (vector)llList2String(parms, 0); + rotation rot = <0.0, 0.0, 0.0, 1.0>; + integer gave_rot = llGetListLength(parms) > 1; + if (gave_rot) rot = (rotation)llList2String(parms, 1); // ooh, they gave us a rotational value also. +// log_it("gave rot? " + (string)gave_rot + " rot=" + (string)rot); + phantom_jaunt_to_list([llGetPos(), v], TRUE, msg, gave_rot, rot); + } else if (msg == FULL_STOP_COMMAND) { + full_stop(); + } else if (msg == REVERSE_VELOCITY_COMMAND) { + reverse_velocity(FALSE); + } else if (msg == SET_VELOCITY_COMMAND) { + vector v = (vector)llList2String(parms, 0); +//log_it("jaunting lib received set velocity request for " + (string)v); + set_velocity(v, FALSE); + } else if (msg == JAUNT_UP_COMMAND) { + phantom_jaunt_to_list([ llGetPos(), llGetPos() + <0.0, 0.0, llList2Float(parms, 0)> ], + TRUE, msg, FALSE, ZERO_ROTATION); + } else if (msg == JAUNT_DOWN_COMMAND) { + phantom_jaunt_to_list([ llGetPos(), llGetPos() + <0.0, 0.0, -llList2Float(parms, 0)> ], + TRUE, msg, FALSE, ZERO_ROTATION); + } else if (msg == JAUNT_LIST_COMMAND) { + string destination_list = llList2String(parms, 0); + list targets = llParseString2List(destination_list, [HUFFWARE_ITEM_SEPARATOR], []); + destination_list = ""; + integer forwards = TRUE; + // snag the directionality for the list, if specified. + if (llGetListLength(parms) > 1) forwards = llList2Integer(parms, 1); + rotation rot = <0.0, 0.0, 0.0, 1.0>; + integer gave_rot = llGetListLength(parms) > 2; + if (gave_rot) rot = llList2Rot(parms, 2); // ooh, they gave us a rotational value also. + phantom_jaunt_to_list(targets, forwards, msg, gave_rot, rot); + targets = []; + } +} + +////////////// +// from hufflets... + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat, but use an unusual channel. +// llSay(108, llGetScriptName() + "--" + (string)debug_num + ": " + to_say); +} +////////////// + +// a simple version of a reply for a command that has been executed. the parameters +// might contain an outcome or result of the operation that was requested. +send_reply(integer destination, list parms, string command) +{ + llMessageLinked(destination, JAUNT_HUFFWARE_ID + REPLY_DISTANCE, command, + llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR)); +} + +// returns TRUE if the value in "to_check" specifies a legal x or y value in a sim. +integer valid_sim_value(float to_check) +{ + if (to_check < 0.0) return FALSE; + if (to_check >= 257.0) return FALSE; + return TRUE; +} + +integer outside_of_sim(vector to_check) +{ + return !valid_sim_value(to_check.x) || !valid_sim_value(to_check.y); +} + +/////////////// + +// returns the portion of the list between start and end, but only if they are +// valid compared with the list length. an attempt to use negative start or +// end values also returns a blank list. +list chop_list(list to_chop, integer start, integer end) +{ + integer last_len = llGetListLength(to_chop) - 1; + if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return []; + return llList2List(to_chop, start, end); +} + +////////////// + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { auto_retire(); } + + link_message(integer sender, integer huff_id, string msg, key id) { + if (huff_id != JAUNT_HUFFWARE_ID) return; // not our responsibility. + handle_link_message(sender, huff_id, msg, id); + } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/jump_good_v6.0.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/jump_good_v6.0.lsl new file mode 100755 index 00000000..a0537fdc --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/jump_good_v6.0.lsl @@ -0,0 +1,318 @@ + +// huffware script: jump good, by fred huffhines +// "jump good" is an homage to samurai jack. +// +// if you wear an object containing this script (and using the +// jump_good_jump_strength() value), then this script will make +// your jumps much higher than normal. other values for the +// jump strength will vary the force on you accordingly. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +////////////// + +// this section provides some example forces one might want to exert. +// all floating point numbers below are measured in meters per second squared, +// which is a measurement of acceleration. + +float EARTH_ACCELERATION = 9.80665; + // the acceleration due to gravity on earth. + +float JUPITER_ACCELERATION = 25.93; + // the acceleration on jupiter due to gravity. + +float earth_jump_strength() { return EARTH_ACCELERATION - EARTH_ACCELERATION; } + // normal gravity for the earth; no additional force added. + +float jupiter_jump_strength() +{ return EARTH_ACCELERATION - (JUPITER_ACCELERATION - EARTH_ACCELERATION); } + // this is what the jump force should be when approximating jupiter's gravity. + +float jump_good_jump_strength() { return 8.4; } + // samurai jack was probably able to jump this high after the tree dwellers + // taught him how to jump good. + +integer DEBUGGING = FALSE; + // if this is true, then extra noise is made about state transitions. + +float TIMER_PERIOD = 4.0; + // how frequently to check our state. + +////////////// + +// this is the most important jump strength, since it's the one we'll use... + +float jump_strength() { return jump_good_jump_strength(); } + // the amount of force that you're "thrown" upwards with. this is + // relative to your mass. the force of gravity pulls on objects with + // a force of 9.80665, so if the jump strength exceeds that, the object + // will never return to earth. + +////////////// + +// global variables... + +key attached_key; // key of the avatar wearing the object, if any avatar is. + +integer _last_seen_flying; // tracks the attached avatar's flight state. + +integer enabled; // records device's state; if false, then it's off. + +////////////// + +// makes the force appropriate to the mass of the overall object. +use_configured_force() +{ + float mass = llGetMass(); +//log_it("mass is " + (string)mass); + if (mass == 0.0) mass = 20000; + // fake there being a good mass. + vector force = <0,0, mass * jump_strength()>; +//log_it("using force of " + (string)force + "."); + llSetForce(force, FALSE); +} + +// resets force to zero to have normal gravity return. +use_zero_force() { +//log_it("using zero force."); + + llSetForce(<0,0,0>, FALSE); } + +// changes the objects current velocity to match "new_velocity". +// if the "local_axis" is true, then it applies the velocity to the +// object's own axis. if false, then it uses the global axis of +// the simulator. +set_velocity(vector new_velocity, integer local_axis) +{ + vector current_velocity = llGetVel(); + + if (local_axis) { + rotation rot = llGetRot(); + current_velocity /= rot; // undo the rotation. + } + + new_velocity -= current_velocity; + new_velocity *= llGetMass(); + + llApplyImpulse(new_velocity, local_axis); +} + +// picks the right amount of force based on whether the object is +// attached to an avatar and whether the avatar is flying. +set_force_appropriately() +{ + if (attached_key != NULL_KEY) { + // make sure we're not flying now; jump force would be redundant. + if (llGetAgentInfo(llGetOwner()) & AGENT_FLYING) { + if (!_last_seen_flying) { +//log_it("avatar wasn't flying, is now. setting zero force."); + use_zero_force(); + } + _last_seen_flying = TRUE; + } else { +//if (_last_seen_flying) { +//log_it("avatar was just flying, but is not now. setting configured force."); +//} + // we're not flying so keep our force set. + _last_seen_flying = FALSE; + use_configured_force(); + + } + } else { + // there's no one attached, so just follow our plan with the force. + use_configured_force(); + } +} + +initialize_forces() +{ + enabled = FALSE; + _last_seen_flying = FALSE; + attached_key = NULL_KEY; + llSetTimerEvent(TIMER_PERIOD); // check avatar's state periodically. + if (llGetAttached() != 0) { + // there's an avatar attached right now! get the stats right. + attached_key = llGetOwner(); + if (llGetAgentInfo(llGetOwner()) & AGENT_FLYING) _last_seen_flying = TRUE; + else _last_seen_flying = FALSE; + } + set_force_appropriately(); +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +////////////// + +// from hufflets... + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + string addition; + if (DEBUGGING) addition = llGetScriptName() + "[" + (string)debug_num + "] "; + llOwnerSay(addition + to_say); + // say this on an unusual channel for chat if it's not intended for general public. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat that anyone can hear. we take off the bling for this one. +// llSay(0, to_say); +} + +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); +//log_it("state_entry"); + initialize_forces(); + } + + timer() { + // we can't do anything without physics enabled. + if (!llGetStatus(STATUS_PHYSICS)) + llSetStatus(STATUS_PHYSICS, TRUE); + if (!llGetStatus(STATUS_PHANTOM)) + llSetStatus(STATUS_PHANTOM, FALSE); + if (enabled) set_force_appropriately(); + else use_zero_force(); + } + + attach(key avatar) + { +//log_it("into attach, key=" + (string)avatar); + initialize_forces(); + } + + on_rez(integer parm) { +//log_it("on_rez"); + initialize_forces(); + } + + touch_start(integer count) { + if (llDetectedKey(0) != llGetOwner()) return; // not listening to that guy. + if (llGetAttached() == 0) return; // cannot change state if not attached. + // flip the current state, since they clicked. + if (enabled) { + log_it("disabled gravity control."); + enabled = FALSE; + use_zero_force(); + } else { + log_it("enabled gravity control."); + enabled = TRUE; + use_configured_force(); + } + } +} + diff --git a/huffware/huffotronic_updater_freebie_v5.3/license__free_in_osgrid_only_v1.8.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/license__free_in_osgrid_&_personal_opensims_only_v1.9.txt similarity index 94% rename from huffware/huffotronic_updater_freebie_v5.3/license__free_in_osgrid_only_v1.8.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/license__free_in_osgrid_&_personal_opensims_only_v1.9.txt index b28cd30d..72dfb50b 100755 --- a/huffware/huffotronic_updater_freebie_v5.3/license__free_in_osgrid_only_v1.8.lsl +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/license__free_in_osgrid_&_personal_opensims_only_v1.9.txt @@ -9,7 +9,7 @@ price in second life: *non-free* ** note: the only avatar licensed to sell materials within this product is fred huffhines in second life. if you bought this from someone else, please let fred huffhines know. price in other grids: *unspecified* - ** we're still developing a relationship with other grids, but any opensim-based-server user can use any of this content on their personal standalone/hypergrid sims for free. we ask that you please do not share any of this content on commercial grids yet. + ** we're still developing a relationship with other grids, but any opensim-based-server user can use any of this content on their personal standalone & hypergrid sims for free. we ask that you please do not share any of this content on commercial grids yet. ** please let us know if you're interested in expanding our licensing to your commercial grid. * our store is called Eclectic Electric Patterns and Widgets (eepaw). we started out in Second life, but have since branched out into opensim based simulators. we have two locations for the store now, one in osgrid that is all ++freebies++, and another store in second life that is a mix of commercial objects and freebies (because the linden labs dingbats insist on charging exorbitant rates to rent space on their servers). diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/license__freebie_in_the_metaverse_v1.8.txt b/huffware/huffotronic_eepaw_knowledge_v60.9/license__freebie_in_the_metaverse_v1.8.txt new file mode 100755 index 00000000..15052c67 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/license__freebie_in_the_metaverse_v1.8.txt @@ -0,0 +1,20 @@ +--license version: april 24 2011-- +author: fred huffhines (osgrid and second life avatar) +source: eepaw shop (see below) +all content by fred huffhines is licensed by GPL [ http://www.gnu.org/licenses/gpl.html ] within the full metaverse (osgrid, opensims, second life, etc). +note that any included content created by others may be licensed differently; all appropriate original copyright notices have been retained. +price: *free* + ** note: if you paid for this, someone ripped you off. please let fred huffhines in osgrid or second life know about this. + +* our store is called Eclectic Electric Patterns and Widgets (eepaw). we started out in Second life, but have since branched out into opensim based simulators. we have two locations for the store now, one in osgrid that is all ++freebies++, and another store in second life that is a mix of commercial objects and freebies (because the linden labs dingbats insist on charging exorbitant rates to rent space on their servers). + +* in osgrid: our brand new freebie store (as of march 2011) is in wright plaza at <228, 22, 21> + [ or http://slurl.com/secondlife/Wright%20Plaza/228/22/21 ]. +this will be our base of operations from now on, or until there's no power left in the global electrical grid. +a lot of our new stuff will be available here first, and for free to osgrid avatars. +please don't upload our stuff into second life, because that store (see below) kind of needs to pay for itself. render unto caesar and all that. so, thanks. + +* in second life: the eepaw shop is located way out in the "smoky" region of the atoll (heterocera). Feel free to mosey over and check our other strange devices out, many others of which are also totally free... + http://slurl.com/secondlife/Smoky/14/84/405 + +* we are personally very fond of each of you. --fred huffhines. diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/mediatron_control_v7.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/mediatron_control_v7.2.lsl new file mode 100755 index 00000000..0855fa5b --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/mediatron_control_v7.2.lsl @@ -0,0 +1,727 @@ + +// huffware script: mediatron control, by fred huffhines. +// +// controls the mediatron device and manages menus for the user. +// when buttons are clicked, this is the script that arranges for things to +// happen. this script also processes the spoken commands from the user. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// configurable constants... + +integer MEDIATRON_CHANNEL = 1008; + // fairly arbitrary channel for listening. + +integer ONLY_OWNER = FALSE; + // only listen to the owner's chats? + +vector TEXT_COLOR = <0.5, 0.7, 0.92>; + // the color that the current chapter is displayed in. + +string LATEX_DRIVER_URL = "http://universalbuilds.com/tib/gif.php?l="; + // the URL for turning tex code into tibetan and english bitmaps. + +// global constants that really should stay constant... + +//////// +// link ids for flinging messages at them. +//----these are useless until the OAR link number bug is fixed. +//integer ROOT_PRIM_LINK = 1; // by SL conventions. +//integer MAIN_VIEWSCREEN_LINK = 2; // the next prim after the root is our viewer. +//integer NEXT_BUTTON_LINK = 3; +//integer PREVIOUS_BUTTON_LINK = 4; +//integer UHHH_BUTTON_LINK = 5; +//integer MENU_BUTTON_LINK = 6; +//integer BACKGROUND_PLATE_LINK = 7; // the blank whitish plank underneath the main viewscreen. +//////// + +integer DEFAULT_CHANNEL = 1008; // this must remain unchanged so we can dynamically modify help. + +// commands that can begin the line of text spoken on our special channel. +// these are also used for interchange with sub-prims, such as when a button is pushed. +string TIBETAN_CHUNK = "tib"; +string ENGLISH_CHUNK = "en"; +string NEW_LINE_CMD = "line"; +string SHOW_BUFFER_CMD = "go"; + +string NEXT_PAGE_CMD = "next"; +string PREVIOUS_PAGE_CMD = "prev"; + +string RESET_MEDIATRON_CMD = "Reset"; +string CLEAR_MEDIATRON_CMD = "Clear"; + +string OPEN_MENU_CMD = "menu"; +string CHAPTER_PICKER_CMD = "chapter"; + +string CHANNEL_CHANGE_CMD = "channel"; +string DRIVER_WEB_SITE_CMD = "urldriver"; +string LOAD_WEB_PAGE_CMD = "loadweb"; + +string TEXT_COLOR_CHANGE_CMD = "textcolor"; + +string HELP_CMD = "Help"; + +// config / control menus that we support. +string STATUS_ITEM = "Status"; +string AUTHORIZE_ITEM = "Authorize"; +string TEXT_COLOR_ITEM = "Text Color"; +string CHANNEL_ITEM = "Channel"; +string WEB_SITE_ITEM = "Driver URL"; + +// API for the viewscreen blitter library... +////////////// +integer VIEWSCREEN_BLITTER_HUFFWARE_ID = 10027; + // unique ID for the viewscreen services. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +// +string SHOW_URL_COMMAND = "#shurl"; + // requests the viewscreen to show a particular URL as its texture. +string RESET_VIEWSCREEN_COMMAND = "#shrz"; + // resets the viewscreen script to the default state. +string SHOW_TEXTURE_COMMAND = "#shtex"; + // displays a texture on the prim. the first parameter is the texture key or name. +////////////// + +// API for moving between the active notecards. +////////////// +// do not redefine these constants. +integer SLATE_READER_HUFFWARE_ID = 10028; + // the unique id within the huffware system for the noteworthy script to + // accept commands on. this is used in llMessageLinked as the num parameter. +////////////// +string RESET_SLATE_READER_COMMAND = "#rsslt"; + // causes the notecard information to be forgotten and the script restarted. +string SR_GET_INFORMATION_COMMAND = "#infy"; + // used by clients to ask for information about the current number of notecards + // available, and their names. this information is sent back on the huffware ID + // plus the reply distance. first parm is the number, and the rest are the names. +string SR_PLAY_CARD_COMMAND = "#playvo"; + // picks a particular notecard for reading and send the notecard's contents in a + // series of link messages, using this command and the reply distance. there are + // two parameters: an integer for the notecard number to read (from 0 through the + // number of notecards - 1) and the link number to send the messages to. +////////////// + +// the button pushing API. +////////////// +integer BUTTON_PUSHER_HUFFWARE_ID = 10029; + // a unique ID within the huffware system for this script. +////////////// +string BUTTON_PUSHED_ALERT = "#btnp"; + // this event is generated when the button is pushed. the number parameter will be + // the huffware id plus the reply distance. the id parameter in the link message will + // contain the name of the button that was pushed. +////////////// + +// menutini link message API... +////////////// +// do not redefine these constants. +integer MENUTINI_HUFFWARE_ID = 10009; + // the unique id within the huffware system for the jaunt script to + // accept commands on. this is used in llMessageLinked as the num parameter. +string SHOW_MENU_COMMAND = "#menu#"; + // the command that tells menutini to show a menu defined by parameters + // that are passed along. these must be: the menu name, the menu's title + // (which is really the info to show as content in the main box of the menu), + // the wrapped list of commands to show as menu buttons, the menu system + // channel's for listening, and the key to listen to. + // the reply will include: the menu name, the choice made and the key for + // the avatar. +////////////// + +// global variables... + +string buffer_text; // the buffer being accumulated for display. + +integer page_number; // current page shown: ranges from zero to number of notecards - 1. + +integer maximum_notecards; // the maximum notecards that can be shown. + +list notecard_choices; // the chapters that can be chosen. + +integer listening_handle; // tracks our listening so we can change channels. + +set_viewscreen_texture() +{ + // get the video texture they want to use for the parcel. + key texture_key = llList2Key(llParcelMediaQuery([PARCEL_MEDIA_COMMAND_TEXTURE]), 0); + if (texture_key == NULL_KEY) { + // there wasn't one set yet. we can't do much about that here. we'll use the + // last inventory texture instead, which usually will be our proper image. + texture_key = llGetInventoryKey(llGetInventoryName(INVENTORY_TEXTURE, + llGetInventoryNumber(INVENTORY_TEXTURE) - 1)); + if (texture_key == NULL_KEY) return; // now we're hosed; there's no texture. + } + // set the main viewer prim to the chosen texture... + llMessageLinked(LINK_SET, VIEWSCREEN_BLITTER_HUFFWARE_ID, + SHOW_TEXTURE_COMMAND, (string)texture_key); +} + +// checks the parcel's media texture to make sure it's set. if it's not, we reset it to +// our favorite version. +fix_parcel_media_texture() +{ + // get the video texture they want to use for the parcel. + key texture_key = llList2Key(llParcelMediaQuery([PARCEL_MEDIA_COMMAND_TEXTURE]), 0); + if (texture_key == NULL_KEY) { + // there wasn't one set yet, so we need to fix that. we'll use the last inventory + // texture, which usually will be our proper image. this is a distinct chore from + // setting the viewscreen's texture, since we don't know that we have the capability + // to change the parcel media texture yet. + texture_key = llGetInventoryKey(llGetInventoryName(INVENTORY_TEXTURE, + llGetInventoryNumber(INVENTORY_TEXTURE) - 1)); + if (texture_key != NULL_KEY) { + llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_TEXTURE, texture_key]); + if (llGetLandOwnerAt(llGetPos()) != llGetOwner()) { + log_it("There is a problem with the parcel media: " + + llGetObjectName() + " is not owned by the parcel owner."); + } else { + log_it("No media texture found for video on this parcel. Using default texture: " + + (string)texture_key); + } + } else { + log_it("The object does not seem to contain any textures; we need at " + + "least one for a default viewscreen image."); + } + } +} + +// asks the slate reader to find out what cards there are for us. +reload_notecards() +{ + // now we find out what's really present so we can offer a menu. + llMessageLinked(LINK_THIS, SLATE_READER_HUFFWARE_ID, + SR_GET_INFORMATION_COMMAND, ""); +} + +// fixes the viewscreen face and parcel media texture, if needed, and gets the mediatron +// ready for commands. +initialize_mediatron() +{ + fix_parcel_media_texture(); + set_viewscreen_texture(); + llSetText("", TEXT_COLOR, 0.0); + page_number = -1; + reload_notecards(); + + // listening for commands from the user. + listening_handle = llListen(MEDIATRON_CHANNEL, "", NULL_KEY, ""); + + llSay(0, "Now listening for commands on channel /" + (string)MEDIATRON_CHANNEL); +} + +// locates the space boundaries between words and returns just a list of the words, +// with no spaces remaining. +list break_on_spaces(string msg) +{ + if (!llStringLength(msg)) return []; // no length means no list. + integer indy; + list to_return; + for (indy = 0; indy < llStringLength(msg); indy++) { + integer space_posn = llSubStringIndex(msg, " "); + if (space_posn < 0) { + // no spaces left, so use the whole string. + to_return += [ msg ]; + indy = 2 * llStringLength(msg); + } else { + // we found a space. chop out the first part to add to the list and + // keep the second part for further scanning. + to_return += [ llGetSubString(msg, 0, space_posn - 1) ]; + // we also trash any additional spaces that were after the one we found. + msg = llStringTrim(llGetSubString(msg, space_posn + 1, -1), STRING_TRIM); + indy = -1; // start over at top of string. + } + } + return to_return; +} + +integer random_channel() { return -(integer)(llFrand(40000) + 20000); } + +simply_display_menu(string menu_name, string title, list buttons) +{ + integer menu_channel = random_channel(); + key listen_to = llGetOwner(); + llMessageLinked(LINK_THIS, MENUTINI_HUFFWARE_ID, SHOW_MENU_COMMAND, + menu_name + HUFFWARE_PARM_SEPARATOR + + title + HUFFWARE_PARM_SEPARATOR + + wrap_item_list(buttons) + HUFFWARE_PARM_SEPARATOR + + (string)menu_channel + HUFFWARE_PARM_SEPARATOR + + (string)listen_to); +} + +// handles when a button on the device has been clicked. +process_button_clicks(string msg, key id) +{ + if (msg != BUTTON_PUSHED_ALERT) return; // uhhhh, not sure what that was. + if (id == NEXT_PAGE_CMD) { + hear_voices(FALSE, "self", llGetOwner(), id); + } else if (id == PREVIOUS_PAGE_CMD) { + hear_voices(FALSE, "self", llGetOwner(), id); + } else if (id == CHAPTER_PICKER_CMD) { + list pruned_choices; + string list_as_block; + integer indy; + for (indy = 0; indy < llGetListLength(notecard_choices); indy++) { + string curr_name = prune_notecard_name(llList2String(notecard_choices, indy)); + pruned_choices += [ curr_name ]; + list_as_block += (string)(indy + 1) + ". " + curr_name; + if (indy < llGetListLength(notecard_choices) - 1) list_as_block += "\n"; + } + simply_display_menu(CHAPTER_PICKER_CMD, "Pick the Chapter to display...\n" + list_as_block, + pruned_choices); + } else if (id == OPEN_MENU_CMD) { + simply_display_menu(OPEN_MENU_CMD, "TibSlate Mediatron Control Menu", + [ HELP_CMD, STATUS_ITEM, AUTHORIZE_ITEM, TEXT_COLOR_ITEM, CHANNEL_ITEM, + WEB_SITE_ITEM, CLEAR_MEDIATRON_CMD, RESET_MEDIATRON_CMD ]); + } +} + +string bool_string_for_int(integer truth) +{ + if (truth) return "true"; else return "false"; +} + +tell_not_authorized() +{ + llSay(0, "The operation you have chosen can only be performed by the owner."); +} + +// handles the response message when the user chooses a button. +react_to_menu(string msg, key id) +{ + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + string menu_name = llList2String(parms, 0); + string which_choice = llList2String(parms, 1); + key av_key = llList2String(parms, 2); + if (ONLY_OWNER && (av_key != llGetOwner())) { + tell_not_authorized(); + return; + } +//log_it("you clicked " + which_choice + " item for av " + av_key); + + if (menu_name == CHAPTER_PICKER_CMD) { + // find the index of the choice if we can, and make the choice be the new page. + page_number = find_similar_in_list(notecard_choices, which_choice); + play_chapter(); + return; + } else if (menu_name == OPEN_MENU_CMD) { + // show the main control menu. + if (which_choice == STATUS_ITEM) { + llSay(0, "Status...\n\tMemory Free " + (string)llGetFreeMemory() + + "\n\tListening Channel " + (string)MEDIATRON_CHANNEL + + "\n\tOwner Only? " + bool_string_for_int(ONLY_OWNER)); + return; + } else if (which_choice == AUTHORIZE_ITEM) { + if (av_key != llGetOwner()) { + // this option isn't allowed for anyone but the owner, ever. + tell_not_authorized(); + return; + } + simply_display_menu(AUTHORIZE_ITEM, "Authorization required to use device...", + [ "Any User", "Only Owner" ]); + return; + } else if (which_choice == TEXT_COLOR_ITEM) { + llSay(0, "To change the text color, say\n\t/" + + (string)MEDIATRON_CHANNEL + " " + TEXT_COLOR_CHANGE_CMD + + " \nwhere r, g, and b are the red, green and blue components of the color.\n" + + "Note that r, g & b can range from 0 to 1 inclusive."); + return; + } else if (which_choice == CHANNEL_ITEM) { + llSay(0, "To change the channel of the viewer, say\n\t/" + + (string)MEDIATRON_CHANNEL + " " + CHANNEL_CHANGE_CMD + + " N\nwhere N is a number for the new channel."); + return; + } else if (which_choice == WEB_SITE_ITEM) { + llSay(0, "Current driver web URL: " + LATEX_DRIVER_URL + + "\nTo replace this site, say:\n\t/" + + (string)MEDIATRON_CHANNEL + " " + DRIVER_WEB_SITE_CMD + + " newURL\nNote that this site must be capable of accepting LaTex code and\n" + + "converting that into simulator compatible bitmaps."); + return; + } else { + // handle all the others by just passing this on to the hear voices function. + // if it's a known command, we handle it. otherwise we bounce it off the couch. + hear_voices(FALSE, "self", llGetOwner(), which_choice); + return; + } + // the remainder of these handle the menu button's menu options... + } else if (menu_name == AUTHORIZE_ITEM) { + // they chose an option for authorization, so see if it was for only the owner. + ONLY_OWNER = is_prefix(which_choice, "only"); + llSay(0, "now, Owner Only = " + bool_string_for_int(ONLY_OWNER)); + return; + } + +log_it("i do not understand item " + which_choice + " in "+ menu_name); +} + +// processes the responses from the slate reader. +handle_slate_items(string msg, key id) +{ + if (msg == SR_PLAY_CARD_COMMAND) { + // we have gotten some data from our reader. + hear_voices(FALSE, "self", llGetOwner(), id); + } else if (msg == SR_GET_INFORMATION_COMMAND) { + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + maximum_notecards = llList2Integer(parms, 0); +//log_it("got info response, num notes=" + (string)maximum_notecards); + notecard_choices = llList2List(parms, 1, -1); + } +} + +// strips off any numerical info to make a more useful notecard name. +string prune_notecard_name(string note_name) +{ + // we look at the name and remove any numbers in front, so we can avoid cluttering up the + // user facing text with the numbers that are used for ordering the notecards. + list rip_these = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", " ", "." ]; + while (llListFindList(rip_these, [ llGetSubString(note_name, 0, 0) ]) >= 0) { + // found a number or space or period. rip it off. + note_name = llDeleteSubString(note_name, 0, 0); + } + return note_name; +} + +show_chapter_name() +{ + string note_name = prune_notecard_name(llList2String(notecard_choices, page_number)); + llSetText("Page #" + (string)(page_number + 1) + + " -- " + note_name, TEXT_COLOR, 1.0); +} + +// shows the contents of the current page's notecard on the viewscreen. +play_chapter() +{ + show_chapter_name(); + llMessageLinked(LINK_THIS, SLATE_READER_HUFFWARE_ID, + SR_PLAY_CARD_COMMAND, wrap_parameters([page_number, LINK_THIS])); +} + +// tell the viewscreen to display this possible tibetan. +// spoken reports whether it's a user speaking or not. if spoken is false, then +// the voice is internal from getting notecard info or button clicks. +hear_voices(integer spoken, string name, key id, string msg) +{ + msg = llStringTrim(msg, STRING_TRIM); // toss extra spaces. + if (msg == CLEAR_MEDIATRON_CMD) { + // wipe out anything on the screen. + set_viewscreen_texture(); + buffer_text = ""; // toss out any partial buffer also. + if (spoken) llSay(0, "Clearing viewscreen."); + page_number = -1; // reset page to the spot before the first. + llSetText("", TEXT_COLOR, 0.0); + return; + } else if ( (msg == RESET_MEDIATRON_CMD) && (id == llGetOwner()) ) { + llMessageLinked(LINK_SET, VIEWSCREEN_BLITTER_HUFFWARE_ID, + RESET_VIEWSCREEN_COMMAND, ""); + llMessageLinked(LINK_THIS, SLATE_READER_HUFFWARE_ID, + RESET_SLATE_READER_COMMAND, ""); + if (spoken) llSay(0, "Resetting scripts."); + llResetScript(); + } else if (is_prefix(msg, CHANNEL_CHANGE_CMD)) { + integer new_channel = (integer)llGetSubString(msg, llStringLength(CHANNEL_CHANGE_CMD), -1); + llSay(0, "New channel is " + (string)new_channel); + MEDIATRON_CHANNEL = new_channel; + llListenRemove(listening_handle); + listening_handle = llListen(MEDIATRON_CHANNEL, "", NULL_KEY, ""); + return; + } else if (is_prefix(msg, TEXT_COLOR_CHANGE_CMD)) { + vector new_col = (vector)llGetSubString(msg, llStringLength(TEXT_COLOR_CHANGE_CMD), -1); + llSay(0, "New text color is " + (string)new_col); + TEXT_COLOR = new_col; + show_chapter_name(); + return; + } else if (is_prefix(msg, DRIVER_WEB_SITE_CMD)) { + string new_url = llGetSubString(msg, llStringLength(DRIVER_WEB_SITE_CMD), -1); + llSay(0, "New driver URL for LaTex: " + (string)new_url); + LATEX_DRIVER_URL = new_url; + return; + } else if (is_prefix(msg, NEXT_PAGE_CMD)) { + page_number++; + if (page_number >= maximum_notecards) { + page_number = 0; + } + play_chapter(); + return; + } else if (is_prefix(msg, PREVIOUS_PAGE_CMD)) { + page_number--; + if (page_number < 0) { + page_number = maximum_notecards - 1; + } + show_chapter_name(); + llMessageLinked(LINK_THIS, SLATE_READER_HUFFWARE_ID, + SR_PLAY_CARD_COMMAND, wrap_parameters([page_number, LINK_THIS])); + return; + } else if (is_prefix(msg, HELP_CMD)) { + provide_help(); + return; + } else if (is_prefix(msg, LOAD_WEB_PAGE_CMD)) { + string new_url = llGetSubString(msg, llStringLength(LOAD_WEB_PAGE_CMD), -1); + llSay(0, "Loading web site: " + (string)new_url); + llMessageLinked(LINK_SET, VIEWSCREEN_BLITTER_HUFFWARE_ID, SHOW_URL_COMMAND, new_url); + buffer_text = ""; // clear the buffer out. + } + + // now we try to process the screen buffer commands which we suspect + // are in the text that was spoken. + list sentence = break_on_spaces(msg); + string first_word = llList2String(sentence, 0); +//log_it("first word is: " + first_word); + string remainder; + integer indo; + for (indo = 1; indo < llGetListLength(sentence); indo++) { + if (indo != 1) remainder += " "; + remainder += llList2String(sentence, indo); + } +//log_it("remainder is: " + remainder); + + if (first_word == TIBETAN_CHUNK) { + if (spoken) llSay(0, "Added Tibetan: " + remainder); + buffer_text += "{\\tib " + remainder + "}{ }"; + } else if (first_word == ENGLISH_CHUNK) { + if (spoken) llSay(0, "Added English: " + remainder); + buffer_text += "{" + remainder + " }"; + } else if (first_word == NEW_LINE_CMD) { + if (spoken) llSay(0, "Inserted blank line."); + buffer_text += "{\\\\}"; + } else if (first_word == SHOW_BUFFER_CMD) { + if (spoken) llSay(0, "Displaying buffered screen."); + // reset text label since that's no longer our page. + if (spoken) llSetText("", TEXT_COLOR, 0.0); + string url = LATEX_DRIVER_URL + buffer_text; + llMessageLinked(LINK_SET, VIEWSCREEN_BLITTER_HUFFWARE_ID, SHOW_URL_COMMAND, url); + buffer_text = ""; // clear the buffer out. + } else { + // treat this as english. +//mostly redundant! + if (spoken) llSay(0, "Added English: " + first_word + " " + remainder); + buffer_text += "{" + first_word + " " + remainder + "}"; + // if we're assuming this is english, we will plug in a blank line afterwards. + // otherwise a text document will look right awful. + buffer_text += "{\\\\}"; + } +} + +// hands out any notecards with help in their names. +provide_help() +{ + integer count = llGetInventoryNumber(INVENTORY_NOTECARD); + integer indy; + list help_cards = []; + for (indy = 0; indy < count; indy++) { + string note_name = llGetInventoryName(INVENTORY_NOTECARD, indy); + if (find_substring(note_name, "help") >= 0) + help_cards += [ note_name ]; + } + + if (llGetListLength(help_cards)) llOwnerSay("Here is the built-in help file..."); + else llOwnerSay("There are currently no help notecards; sorry."); + // iterate on helper cards and give them out. + count = llGetListLength(help_cards); + for (indy = 0; indy < count; indy++) { + string note_name = llList2String(help_cards, indy); + llGiveInventory(llGetOwner(), note_name); + } +} + + +////////////// +// from hufflets... + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on an unusual channel for chat if it's not intended for general public. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat that anyone can hear. we take off the bling for this one. +// llSay(0, to_say); +} + +////////////// + +// joins a list of parameters using the parameter sentinel for the library. +string wrap_parameters(list to_flatten) +{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); } + +// joins a list of sub-items using the item sentinel for the library. +string wrap_item_list(list to_wrap) +{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); } + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return (llSubStringIndex(llToLower(compare_with), llToLower(prefix)) == 0); } + +// returns the index of the first occurrence of "pattern" inside +// the "full_string". if it is not found, then a negative number is returned. +integer find_substring(string full_string, string pattern) +{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); } + +// locates the string "text" as a partial match in the list to "search_in". +integer find_similar_in_list(list search_in, string text) +{ + integer len = llGetListLength(search_in); + integer i; + for (i = 0; i < len; i++) { + if (find_substring(llList2String(search_in, i), text) >= 0) + return i; + } + return -1; +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +// end hufflets. +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { + auto_retire(); + initialize_mediatron(); + } + + listen(integer chan, string name, key id, string msg) { + if (chan != MEDIATRON_CHANNEL) return; // how are we even hearing that? + if (ONLY_OWNER && (id != llGetOwner())) return; // unauthorized talker. + hear_voices(TRUE, name, id, msg); + } + + on_rez(integer start_parm) { + llResetScript(); + } + + changed(integer change) { + if (change & CHANGED_INVENTORY) { + reload_notecards(); + } + } + + link_message(integer sender, integer num, string msg, key id) { + if (num == BUTTON_PUSHER_HUFFWARE_ID + REPLY_DISTANCE) { + process_button_clicks(msg, id); + return; + } + if (num == MENUTINI_HUFFWARE_ID + REPLY_DISTANCE) { + react_to_menu(msg, id); + } + + if (num != SLATE_READER_HUFFWARE_ID + REPLY_DISTANCE) return; // not for us. + handle_slate_items(msg, id); + } +} + diff --git a/huffware/huffotronic_updater_freebie_v5.3/menu_list_manager_v12.4.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/menu_list_manager_v12.4.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/menu_list_manager_v12.4.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/menu_list_manager_v12.4.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/menutini_library_v5.9.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/menutini_library_v5.9.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/menutini_library_v5.9.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/menutini_library_v5.9.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/minnow_v10.6.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/minnow_v10.6.lsl new file mode 100755 index 00000000..dd18ac3c --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/minnow_v10.6.lsl @@ -0,0 +1,395 @@ + +// huffware script: minnow, modifications by fred huffhines. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// +// original attributions: +// Fixit Galatea generic boat script +// This script uses the basic direction keys for boat operation and requires +// the final prim to be oriented at rotation <0, 0, 0>. + +//**************************************************************************** +//**************************************************************************** +//**************************************************************************** +//* The following items can be edited in this script. See the comment for each +//* item to see how it work when it is changed. +//**************************************************************************** + +//* When the forward key is pressed and the mouse button is pressed at the +//* same time, this speed is used. This number should be between 0.0 and 30.0, +//* with higher numbers giving faster performance. +float fastspeed = 16.0; + +//* When the forward key is pressed and the mouse button is NOT pressed, this +//* speed is used. This number should be between 0.0 and whatever the +//* fastspeed value was set at, with higher numbers giving faster performance. +float slowspeed = 8.0; + +//* This is the reverse speed. It shouldn't be too fast, but can be between +//* 0.0 and 30.0. +float backspeed = 2.0; + +//* This is the vehicle 'hover' height, or how far it sits out of the water. +//* Adjust this as necessary for your boat. +float floatheight = 0.44; + +//* If the boat is to ramp up speed slowly, set the following value to '1'. +//* Otherwise, the board will instantly reach full speed when the forward +//* key is pressed. +integer fastforward = 0; // NOT IMPLEMENTED YET + +//* Turning speed, a small turning speed is best +float turnspeed = 1.5; + +//* If the boat 'angles' into turns, set the following value to '1'. +//* Otherwise, the boat will do flat turns. +integer angledturn = 1; // NOT IMPLEMENTED YET + +//* Height offset of seat +//vector seatheight = <.2, 0, 0.35>; + +vector camera_lives_at = <-7, 0, 3>; + // where the camera is poised when the avatar is seated. + +vector camera_looks_at = <4, 0, 1>; + +//**************************************************************************** +//**************************************************************************** +//**************************************************************************** + +// Initialize the vehicle as a boat +vehicle_init() +{ + llSetVehicleType(VEHICLE_TYPE_BOAT); + + // least for forward-back, most friction for up-down + llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <10, 1, 3>); + + // uniform angular friction (setting it as a scalar rather than a vector) + llSetVehicleFloatParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE, 0.1); + + // linear motor wins after about five seconds, decays after about a minute + llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0>); + llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, 0.1); + llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 0.05); + + // agular motor wins after four seconds, decays in same amount of time + llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, <0, 0, 0>); + llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE, 0.1); + llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 0.2); + + // hover + llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, floatheight); + llSetVehicleFloatParam(VEHICLE_HOVER_EFFICIENCY, 0.2); + llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 0.4); + llSetVehicleFloatParam(VEHICLE_BUOYANCY, 1.0); + + // Slight linear deflection with timescale of 1 seconds + llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.1); + llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 1); + + // Slight angular deflection + llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.1); + llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 6); + + // somewhat bounscy vertical attractor + llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.5); + llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 1); + + // weak negative damped banking + llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY, 1.0); + llSetVehicleFloatParam(VEHICLE_BANKING_MIX, 1.0); + llSetVehicleFloatParam(VEHICLE_BANKING_TIMESCALE, 0.1); + + // default rotation of local frame + llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME, + llEuler2Rot(<0, 0, 0>)); + + // remove these flags. +//disabled due to opensim problems running this call. +// llRemoveVehicleFlags(VEHICLE_FLAG_HOVER_TERRAIN_ONLY +// | VEHICLE_FLAG_LIMIT_ROLL_ONLY +// | VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT); + + // set these flags +// llSetVehicleFlags(VEHICLE_FLAG_NO_DEFLECTION_UP +// | VEHICLE_FLAG_HOVER_WATER_ONLY +// | VEHICLE_FLAG_HOVER_UP_ONLY +// | VEHICLE_FLAG_LIMIT_MOTOR_UP); +} + +// Setup everything +all_setup() +{ + // Display version number + llWhisper(0, "running script " + llGetScriptName()); + llSetStatus(STATUS_PHYSICS, FALSE); + + // Set sit direction (forward) and sight location slightly up and behind +// llSitTarget(seatheight, llEuler2Rot(<0, 0, 0>)); + llSetCameraAtOffset(camera_looks_at); + llSetCameraEyeOffset(camera_lives_at); + llSetSitText("Pilot"); + + // Initialize vehicle states + vehicle_init(); + + // Set up listener callback function + llListen(0, "", llGetOwner(), ""); +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +// State (default) event handlers +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { + auto_retire(); + all_setup(); + } + + on_rez(integer start_param) + { + llSetStatus(STATUS_PHYSICS, FALSE); + } + + run_time_permissions(integer permissions) + { + // Get user permissions + if ((permissions & PERMISSION_TAKE_CONTROLS) == + PERMISSION_TAKE_CONTROLS) + { + llTakeControls(CONTROL_ML_LBUTTON | CONTROL_FWD | + CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT | + CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT, TRUE, FALSE); + } + } + + changed(integer change) + { + if (change & CHANGED_LINK) + { + key agent = llAvatarOnSitTarget(); + if (agent) + { + if (agent != llGetOwner()) + { + llSay(0, "This boat isn't yours, but you can buy a copy!"); + llUnSit(agent); + } + else + { + llRequestPermissions(agent, PERMISSION_TAKE_CONTROLS); + llSetStatus(STATUS_PHYSICS, TRUE); + } + } + else + { + llReleaseControls(); + llSetStatus(STATUS_PHYSICS, FALSE); + } + } + } + + control(key name, integer levels, integer edges) + { + float side = 0.0; + float forw = 0.0; + float move = 0.0; + float turn; + + if (levels & CONTROL_ML_LBUTTON) + { + move = fastspeed; + turn = 1.5 * turnspeed; +// forw = 2 * PI / 3; + } + else if (levels & CONTROL_FWD) + { + move = slowspeed; + turn = 1.0 * turnspeed; +// forw = PI / 2; + } + else if (levels & CONTROL_BACK) + { + move = -backspeed; +// forw = -PI / 3; +// turn = -1.0 * turnspeed; + } + + if (levels & (CONTROL_LEFT | CONTROL_ROT_LEFT)) + { + if (move == fastspeed) + { + side = turnspeed; +// forw = PI; + } + else if (move != 0) + { + side = turnspeed; +// forw = PI / 3; + } + else + { + side = .67 * turnspeed; +// forw = PI / 4; + move = 0.1; + } + } + else if (levels & (CONTROL_RIGHT | CONTROL_ROT_RIGHT)) + { + if (move == fastspeed) + { + side = -turnspeed; +// forw = PI; + } + else if (move != 0) + { + side = -turnspeed; +// forw = PI / 3; + } + else + { + side = -.67 * turnspeed; +// forw = PI / 4; + move = 0.1; + } + } + + if (move == 0) + { + llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <0, 0, 0>); + } + else + { + llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, + ); + llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, + <-side / 5, 0, side>); + } + } + + listen(integer channel, string name, key id, string message) + { + list params = llParseString2List(message, [" "], [":"]); + string cmd = llList2String(params, 0); + integer enable = llList2Integer(params, 1); + + // Commands + ; + } + + touch_start(integer total_number) + { + key id = llDetectedOwner(total_number - 1); + // llGiveInventory(id, "Info card"); + } +} diff --git a/huffware/huffotronic_updater_freebie_v5.3/non-script_giver_v2.8.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/non-script_giver_v2.8.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/non-script_giver_v2.8.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/non-script_giver_v2.8.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/noteworthy_library_v12.4.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/noteworthy_library_v12.4.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/noteworthy_library_v12.4.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/noteworthy_library_v12.4.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/particle_projector_v3.1.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/particle_projector_v3.1.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/particle_projector_v3.1.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/particle_projector_v3.1.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/party_culiar_v5.7.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/party_culiar_v5.7.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/party_culiar_v5.7.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/party_culiar_v5.7.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/phantomizer_v1.6.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/phantomizer_v1.6.lsl new file mode 100755 index 00000000..3efd92bf --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/phantomizer_v1.6.lsl @@ -0,0 +1,165 @@ + +// huffware script: phantomizer, by fred huffhines +// +// turns the object that contains this into a phantom on the schedule specified below. +// note that this should not be done to physical objects or they will sink into whatever +// they were sitting on. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// constants... + +float SECONDS_per_minute = 60.0; + +// variables... + +integer solid; // a boolean that represents our current solidity (TRUE=solid). + +// constant-ish functions. + +float solid_duration() // how long to be solid, in seconds. +{ return 4.20 * SECONDS_per_minute; } +float phantom_duration() // how long to be ghosty, in seconds. +{ return 14.0; } + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { + auto_retire(); + solid = FALSE; + llSetStatus(STATUS_PHANTOM, TRUE); +//llWhisper(0, "resetting and taking on phantom state."); + llSetTimerEvent(0); + llSetTimerEvent(phantom_duration()); + } + + timer() + { + if (solid) { + // we turn ethereal now. + solid = FALSE; +llWhisper(0, "becoming a phantom"); + llSetStatus(STATUS_PHANTOM, TRUE); + llSetTimerEvent(0); + llSetTimerEvent(phantom_duration()); + } else { + // time to solidify. + solid = TRUE; +llWhisper(0, "becoming solid"); + llSetStatus(STATUS_PHANTOM, FALSE); + llSetTimerEvent(0); + llSetTimerEvent(solid_duration()); + } + } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/puffer_v1.3.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/puffer_v1.3.lsl new file mode 100755 index 00000000..99d575d0 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/puffer_v1.3.lsl @@ -0,0 +1,186 @@ + +// huffware script: puffer, by fred huffhines +// +// causes the prim that this script is located in to enlarge periodically. this is +// mostly concerned with physical objects that are autonomous and can get stuck in +// prims as they move around. thus the script also tracks the current position and +// ensures that it keeps changing. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// constants... + +vector NORMAL_SIZE = <1.0, 1.0, 0.04>; // normal size when we're not puffing. +vector PUFFED_SIZE = <1.0, 1.0, 1.0>; // size that we blow out to when puffing up. + +float PUFFING_PERIOD = 8.4; // number of seconds between puff-ups. +float STAY_PUFFED_TIME = 1.4; // number of seconds to remain puffed up. + +float PANIC_HOP_DISTANCE = 1.0; // meters to jump upwards if feeling stuck. +float MINIMAL_DISTANCE_OF_MOTION = 0.1; // must move at least this much to be considered unstuck. + +// variables... + +vector last_position; // tracks where the object is hanging out. + +// sets the object size to a new value. to do this, we need to turn off physics +// temporarily. +change_size(vector new_size) +{ + llSetStatus(STATUS_PHYSICS, FALSE); + llSetPrimitiveParams([PRIM_SIZE, new_size]); + llSetStatus(STATUS_PHYSICS, TRUE); +} + +// jumps upwards by a bit to try to escape fat prims we might get stuck inside of. +hop_up() +{ + llSetStatus(STATUS_PHYSICS, FALSE); + llSetPos(); + llSetStatus(STATUS_PHYSICS, TRUE); +} + +// this is our whole process for puffing out here. we will make sure to +// change size briefly for the puffing. we also track the last place we +// were at when puffing and if it's too close to our current position, +// then we'll jump up a bit. +emulate_puffer_fish() +{ + change_size(PUFFED_SIZE); + list siz = llGetPrimitiveParams([PRIM_SIZE]); +//llOwnerSay("size puffed=" + llList2String(siz, 0)); + llSleep(STAY_PUFFED_TIME); // snooze a bit. + change_size(NORMAL_SIZE); + siz = llGetPrimitiveParams([PRIM_SIZE]); +//llOwnerSay("size normal=" + llList2String(siz, 0)); + if (llVecDist(last_position, llGetPos()) < MINIMAL_DISTANCE_OF_MOTION) { + // we seem to be stuck, so hop up a bit and see if that helps. + hop_up(); + } + // reset our last position to where we are now. + last_position = llGetPos(); +} + +////////////// +// from hufflets... + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.8. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { + auto_retire(); + last_position = llGetPos(); + change_size(NORMAL_SIZE); + llSetTimerEvent(PUFFING_PERIOD); // puff out this frequently. + } + + on_rez(integer parm) { llResetScript(); } + + timer() { emulate_puffer_fish(); } +} + diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/radio_tuner_v1.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/radio_tuner_v1.2.lsl new file mode 100755 index 00000000..406a998c --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/radio_tuner_v1.2.lsl @@ -0,0 +1,174 @@ + +// huffware script: radio tuner, by fred huffhines +// +// a radio tuner with capability for overriding the channel. +// this is useful if you want it to stay tuned on a particular channel. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +integer force_channel = 0; // set to non-zero to force the channel to stay set. +integer channel_stays_on = 69; // the channel that the radio will stay stuck on when forced. + +integer listen_handle = 0; +integer channel = 0; + +//hmmm: really broken implementation. +// need to support multiple channels. +set_radio_channel(integer chan) +{ + channel = chan; + + if (listen_handle) + llListenRemove(listen_handle); + + if (!chan) + { + listen_handle = 0; + llOwnerSay("Radio turned off."); + return; + } + + listen_handle = llListen(chan, "", NULL_KEY, ""); + llOwnerSay("Listening to channel " + (string)chan); +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { + auto_retire(); +//hmmm: logic broken here. subscribe to a default set. let them add and remove from set. + if (!force_channel) { +//hmmm: terrible defaults treatment. + llListen(96, "", llGetOwner(), ""); + llOwnerSay("Type \"/96 tune N\" to tune in to channel N, or \"/96 tune 0\" to turn radio off."); + } else { + llOwnerSay("Channel locked on " + (string)channel_stays_on + "."); + set_radio_channel(channel_stays_on); + } + } + + attach(key attached) + { + if (attached != NULL_KEY) + llResetScript(); + } + + listen(integer ch, string name, key id, string msg) + { + if (!force_channel && ((ch == 96) && llGetSubString(msg, 0, 4) == "tune ") ) + set_radio_channel((integer)llGetSubString(msg, 5, -1)); + else if (ch == channel) + llOwnerSay("[" + (string)ch + " " + name + "]: " + msg); + } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/rezzeroni_v20.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/rezzeroni_v20.2.lsl new file mode 100755 index 00000000..0e162579 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/rezzeroni_v20.2.lsl @@ -0,0 +1,1061 @@ + +// huffware script: rezzeroni, by fred huffhines +// +// a script that rezzes objects for you according to some game plan, which is defined +// in a notecard. this script can also run periodically to create temporary objects +// on a schedule. the real power of this script comes from its ability to rez up a +// collection of objects using relative positioning. the newly rezzed objects can be +// given a startup notecard also, which is held inside the rezzer. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// * note regarding the periodic rezzing capability: the objects that are created +// really need to be marked as temporary objects. otherwise the newly rezzed +// objects will clutter up the sim, flooding it with objects, and will eventually +// go over the owner's prim limit. that's not cool. so if you use a periodic +// rezzer, make sure your contained objects are temporaries. + +// global constants... + +integer DEBUGGING = FALSE; // produces noisy diagnostics if enabled. + +integer SUPER_NOISY_DEBUG = FALSE; // extraordinarily noisy items are logged. + +float HOME_PROXIMITY = 0.1; // object must be this close to home before we quit. + +integer PERFORM_JAUNTS = TRUE; // set to true if rezzer should teleport to rez locations. + +integer OBJECT_STARTUP_PARM = 14042; +//hmmm: may want this to come from notecard someday. + +// notecard configurable variables: +integer owner_only; // is the owner the only one allowed to run this rezzer? +vector obj_position; // absolute position for object to rez at. +integer obj_is_singleton; // should the object not be rezzed if similar named one exists? +//hmmm: support configurable singleton sensor range? +integer rezzed_at_once; // the number of each object that are rezzed at the same time. +float obj_rezzing_period; // how often the objects are rezzed up, in seconds. +integer max_run_time; // how long to run once enabled, in seconds. 1200=20 minutes. +string gift_card_name; // a gift object placed inside newly rezzed object. +vector obj_max_offset_add = <2.0, 2.0, 2.0>; // random offsets can be this large. +vector obj_max_rotation_add = ; // max random rotation. +// these object variables can be randomized. "random" means pick a random item. +string obj_name; // what is the object called? "ALL" means all items. +string obj_offset; // how far away from current position object rezzes. +string obj_rotation; // euler rotation for object to point at upon rez. +string obj_multi_offset; // the amount to add to the offset each time for count > 1. +string obj_multi_angle; // the angle added to each rez. +//hmmm: also allow position to be random? +integer DIE_ON_DEMAND_CHANNEL = 4826; // default, can be loaded from notecard. + +// assorted global variables... +integer is_enabled_now; // records whether the rezzer is running a plan right now. +integer when_last_enabled; // tracks time when device was turned on. +// notecard globals. +string global_notecard_name; // name of our notecard in the object's inventory. +integer response_code; // set to uniquely identify the notecard read in progress. +list global_config_list; // a collection of configuration parameters from our notecard. +// globals tracking the current rezzing run. +list already_done_items; // the items that are finished being rezzed, since they have no rate. +integer rezzed_overall; // a count of the number of rezzes during this run. +list gift_stack; // a stack of items to hand to newly rezzed objects. +// somewhat ugly rez plan globals. +integer global_config_index; // where are we in the config list? +string global_singleton_checker; // is a singleton object being sensor checked? +integer global_found_the_singleton; // did we see the singleton object requested? +integer sensor_checks_pending; // how many sensor scans are awaited (zero or one). +integer recent_items_rezzed; // how many got rezzed in last run? +// object creation loop vars: +integer current_object_index = 0; // which object are we at, in the current section? +integer current_count; // the number already created of the current object. + +vector main_home; + // the home location for the rezzer object. it will return to this after + // performing the requested rezzes. +rotation main_rotate; + // original rotation of the rezzer object. + +integer jaunt_responses_awaited; + // the number of jumps still pending. should never be more than one in this script. + +// constants that should generally not be modified unless you are an expert... + +string REZZERONI_SIGNATURE = "#rezzeroni"; // the expected first line of our notecards. + +float MAXIMUM_REZ_DISTANCE = 9.9; + // the farthest distance that we can rez an object at reliably. if the distance is + // larger than this, then the rezzer will teleport to the work zone. + +float BASE_TIMER_PERIOD = 0.04; // the rate at which the timer runs to rez objects. + +// requires noteworthy library v9.3 or better. +////////////// +// do not redefine these constants. +integer NOTEWORTHY_HUFFWARE_ID = 10010; + // the unique id within the huffware system for the noteworthy script to + // accept commands on. this is used in llMessageLinked as the num parameter. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +// commands available via the noteworthy library: +string NOTECARD_READ_CONTINUATION = "continue!"; + // returned as first parameter if there is still more data to handle. +// commands available via the noteworthy library: +string READ_NOTECARD_COMMAND = "#read_note#"; + // command used to tell the script to read notecards. needs a signature to find + // in the card as the first parameter, and a randomly generated response code for + // the second parameter. the response code is used to uniquely identify a set of + // pending notecard readings (hopefully). the signature can be blank. + // the results will be fired back as the string value returned, which will have + // as first element the notecard's name (or BAD_NOTECARD_INDICATOR if none was + // found) and as subsequent elements an embedded list that was read from the + // notecard. this necessarily limits the size of the notecards that we can read + // and return. +string READ_SPECIFIC_NOTECARD_COMMAND = "#read_thisun#"; + // like the read notecard command, but specifies the notecard name to use. only that + // specific notecard file will be consulted. first and second parm are still signature + // and response code, third parm is the notecard name. +// +////////////// +// joins a list of parameters using the parameter sentinel for the library. +string wrap_parameters(list to_flatten) +{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); } +////////////// + +// looks for a notecard with our signature in it and reads the configuration. +// an empty string is returned if we couldn't find anything. +request_configuration() +{ + global_notecard_name = ""; + response_code = 0; + + // try to find a notecard with our configuration. + response_code = -1 * (integer)randomize_within_range(23, 80000, FALSE); + string parms_sent = wrap_parameters([REZZERONI_SIGNATURE, response_code]); + llMessageLinked(LINK_THIS, NOTEWORTHY_HUFFWARE_ID, READ_NOTECARD_COMMAND, + parms_sent); +} + +// processes link messages received from support libraries. +integer handle_link_message(integer which, integer num, string msg, key id) +{ + list parms; + if (num == JAUNT_HUFFWARE_ID + REPLY_DISTANCE) { + // did we get a response to a teleport request? + if (msg == JAUNT_LIST_COMMAND) { +//log_it("msg: " + (string)num + " str=" + str); + jaunt_responses_awaited--; // one less response being awaited. + if (jaunt_responses_awaited < 0) { + log_it("error, jaunts awaited < 0!"); + jaunt_responses_awaited = 0; + } + // unpack the reply. + parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + integer last_jaunt_was_success = llList2Integer(parms, 0); + vector posn = (vector)llList2String(parms, 1); + //log_it("got a reply for a jaunt request, success=" + (string)last_jaunt_was_success + " posn=" + (string)posn); + //do anything with the parms? + } + return FALSE; + } + if ( (num != NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE) + || (msg != READ_NOTECARD_COMMAND) ) return FALSE; // not for us. + + // process the result of reading the notecard. + parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + string notecard_name = llList2String(parms, 0); + integer response_for = llList2Integer(parms, 1); + if (response_for != response_code) return FALSE; // oops, this isn't for us. + // make sure if we are being told to keep going. + if (notecard_name == NOTECARD_READ_CONTINUATION) { +//log_it("continuation of notecard seen."); + // snag all but the first two elements for our config now. + global_config_list += llList2List(parms, 2, -1); + // we're not done reading yet, so fall out to the false return. + } else if (notecard_name != "bad_notecard") { + // a valid notecard has been found and we're done with it now. + global_notecard_name = notecard_name; + // snag all but the first two elements for our config now. + global_config_list += llList2List(parms, 2, -1); + global_config_index = 0; + // signal that we're done getting the config. + return TRUE; + } else { + // we hated the notecards we found, or there were none. + log_it("sorry, no notecards starting with '" + + REZZERONI_SIGNATURE + + "'; cannot rez yet."); + } + return FALSE; +} + +/////////////// + +// requires jaunting library v10.5 or greater. +////////////// +// do not redefine these constants. +integer JAUNT_HUFFWARE_ID = 10008; + // the unique id within the huffware system for the jaunt script to + // accept commands on. this is used in llMessageLinked as the num parameter. +// commands available via the jaunting library: +string JAUNT_COMMAND = "#jaunt#"; + // command used to tell jaunt script to move object. pass a vector with the location. +string JAUNT_LIST_COMMAND = "#jauntlist#"; + // like regular jaunt, but expects a list of vectors as the first parameter; this list + // should be in the jaunter notecard format (separated by pipe characters). + // the second parameter, if any, should be 1 for forwards traversal and 0 for backwards. +// +////////////// +// encases a list of vectors in the expected character for the jaunting library. +string wrap_vector_list(list to_wrap) +{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); } +////////////// + +// asks the jaunting library to take us to the target using a list of waypoints. +request_jaunt(list full_journey, integer forwards) +{ + if (PERFORM_JAUNTS) { + jaunt_responses_awaited++; + llMessageLinked(LINK_THIS, JAUNT_HUFFWARE_ID, JAUNT_LIST_COMMAND, + wrap_vector_list(full_journey) + + HUFFWARE_PARM_SEPARATOR + (string)forwards); + } +} + +// jaunts back to our home location. +attempt_to_go_home() +{ + // jump back to home. + request_jaunt([llGetPos(), main_home], TRUE); +} + +/////////////// + +// creates the real list of items to rez based on the obj_name variable. +// this might turn out to be an empty list. +list build_rezzing_list() +{ + list objects_to_rez; // what items are we expected to create in this round? + + integer num_inv = llGetInventoryNumber(INVENTORY_OBJECT); + if (num_inv == 0) { + log_it("Inventory is empty; please add objects to rez."); + llSetTimerEvent(0.0); // stop the timer; nothing further to do. + return []; + } +//hmmm: all has never been tested. + if (obj_name == "ALL") { + integer inv; + for (inv = 0; inv < num_inv; inv++) { + objects_to_rez += [ llGetInventoryName(INVENTORY_OBJECT, inv) ]; + } + obj_is_singleton = FALSE; // flag makes no sense for multiple objects. + } else { + // we know we are not being told to do a multiple set of objects. + if (obj_name == "random") { + // special keyword for a random object. + integer inv = (integer)randomize_within_range(0.0, num_inv, FALSE); + obj_name = llGetInventoryName(INVENTORY_OBJECT, inv); + if (SUPER_NOISY_DEBUG) + log_it("chose item #" + (string)inv + " randomly (called '" + obj_name + "')."); + } else { + // a normal singular object to create. + if (find_in_inventory(obj_name, INVENTORY_OBJECT) < 0) { + // this item is bogus! + log_it("item '" + obj_name + "' is bogus! not in inventory."); + obj_name = ""; // reset it. + } + } + // now make sure we didn't already do this one. + if (find_in_list(already_done_items, obj_name) >= 0) { +// if (SUPER_NOISY_DEBUG) +// log_it("item '" + obj_name + "' has already been rezzed. skipping it."); + obj_name = ""; + } else { + if (obj_name != "") objects_to_rez += [ obj_name ]; + } + } + + return objects_to_rez; +} + +// checks whether the object is even supposed to be a singleton or not. +// if it should be a singleton, then we run a sensor ping to see if it exists in the +// vicinity or not. if it's not there, we'll eventually hear about it and create it. +integer validate_singleton_object() +{ + // see if there's only supposed to be one of these objects. + if (!obj_is_singleton) return TRUE; // just keep going; it's not one. + if (obj_name == "") return TRUE; // this should never happen. pretend it's okay. + + // okay, we know we should have a singleton. now did we already check for its presence? + if (global_singleton_checker == "") { + // have not scanned yet; postpone the rezzing action and start a sensor for the object. + sensor_checks_pending++; + global_singleton_checker = obj_name; + // maximally distant ping in full arc on all types of objects we can see. + llSensor(obj_name, NULL_KEY, AGENT|ACTIVE|PASSIVE, 96, PI); + if (DEBUGGING) log_it("scanning for singleton item '" + obj_name + "' now."); + return FALSE; + } else { + // we should have finished a sensor probe now, since there were no checks pending + // and since this is a singleton. + if (global_found_the_singleton) { + if (DEBUGGING) + log_it("singleton item '" + obj_name + "' already present; not re-rezzing."); + already_done_items += [ obj_name ]; +//if (TERMINATE_IF_PRESENT) +string DIE_ON_DEMAND_MESSAGE = "die-on-demand"; +llShout(DIE_ON_DEMAND_CHANNEL, DIE_ON_DEMAND_MESSAGE); + } else { + if (DEBUGGING) + log_it("singleton item '" + obj_name + "' is absent; will rez one now if possible."); + } + return TRUE; + } +} + +// operates on the current parameters to rez an object or objects. +integer perform_rezzing_action() +{ + vector current_home = llGetPos(); + list objects_to_rez = build_rezzing_list(); +//log_it("rezzing " + (string)llGetListLength(objects_to_rez) + " objects"); + // create the specified number of the items we were told to use. + integer num_objects = llGetListLength(objects_to_rez); + if (num_objects == 0) { + // nothing to do at this point. +// if (SUPER_NOISY_DEBUG) log_it("no items in rez list; bailing out."); + return TRUE; + } + // iterate across each object in inventory. + integer inv; + for (inv= current_object_index; inv < num_objects; inv++) { +//log_it("object index " + (string)inv); + // rez as many of the object as we were requested to. + string new_rezzee = llList2String(objects_to_rez, inv); + integer counter; + for (counter = current_count; counter < rezzed_at_once; counter++) { + // make sure we've never rezzed a non-periodic with this name before. + if (find_in_list(already_done_items, new_rezzee) < 0) { +///log_it("haven't rezzed '" + new_rezzee + "' yet."); + vector rez_place = obj_position; +//log_it("configd rezplace=" + (string)rez_place); + if (rez_place == <0.0, 0.0, 0.0>) { + // they did not give us an absolute position, so use the home position. + rez_place = main_home; +//log_it("rehomed rezplace=" + (string)rez_place); + } + vector calc_offset = maybe_randomize_offset(obj_offset, obj_max_offset_add); + if (calc_offset != <0.0, 0.0, 0.0>) { + // the offset is non-zero, so apply it to the rez place. + rez_place += calc_offset * main_rotate; +//log_it("offsetd rezplace=" + (string)rez_place); + } + // add in the multiplier offset if this is not the first rezzing of this item. + rez_place += ((float)counter) + * maybe_randomize_offset(obj_multi_offset, obj_max_offset_add) + * main_rotate; +//log_it("home=" + (string)main_home + " rezplace=" + (string)rez_place); + + // check that we are not too far away. + float rez_distance = llVecDist(rez_place, llGetPos()); + if (rez_distance > MAXIMUM_REZ_DISTANCE) { + // try to jump there if we're too far away to rez from here. + if (DEBUGGING) + log_it("distance is " + (string)rez_distance + + "; jaunting closer."); + request_jaunt([llGetPos(), rez_place], TRUE); + current_count = counter; + current_object_index = inv; + return FALSE; + } + + // make sure we aren't already done with this object, if it's a singleton. + integer keep_going = validate_singleton_object(); + if (!keep_going) { + if (SUPER_NOISY_DEBUG) log_it("must sense for singleton, bailing."); + current_count = counter; + current_object_index = inv; + return FALSE; // we can't proceed yet. must check for singleton. + } + +//hmmm: we need to offer an angular offset also! +// this would mean that the multiple guys could be set at nice different angles. + + // now check again whether to rez this or not; the validation method adds it to the + // done list if the object is already present and is a singleton. + if (find_in_list(already_done_items, new_rezzee) < 0) { + if (DEBUGGING) + log_it("now rezzing '" + new_rezzee + "', rot " + obj_rotation); + vector rota = maybe_randomize_rotation(obj_rotation, obj_max_rotation_add); + rotation real_rot = llEuler2Rot(rota * DEG_TO_RAD); + +//hmmm: might be nice to offer an absolute version! +// that accounts for the specific position and offset, rather +// than relative to rezzer. angle should be kept absolutist also. + + // add in our rotation component as the rezzer. + real_rot *= main_rotate; +//this is bunkum. + // add in the angular addition for multiple object creation. + real_rot *= llEuler2Rot( + maybe_randomize_rotation(obj_multi_angle, obj_max_rotation_add) + * (float)counter * DEG_TO_RAD); + recent_items_rezzed++; // we got to do something. + // rez it up since we know what is wanted and where, plus we're in range. + llRezObject(new_rezzee, rez_place, ZERO_VECTOR, real_rot, OBJECT_STARTUP_PARM); + rezzed_overall++; // one more item. + if (gift_card_name != "") { + // this guy needs its startup gift. + string inv_name = gift_card_name + (string)rezzed_overall; + if (find_in_inventory(inv_name, INVENTORY_NOTECARD) >= 0) { + // this inventory item exists, so use it. + gift_stack += [ inv_name]; + } + } + global_singleton_checker = ""; // reset singleton checker for next object. + } else { + if (SUPER_NOISY_DEBUG) log_it("not rezzing, already saw it in list"); + } + } + } + current_count = 0; // got to the full count for the object. + // if this is a non-periodic object, we don't need any more runs. + if (obj_rezzing_period == 0.0) { + if (find_in_list(already_done_items, new_rezzee) < 0) { + if (SUPER_NOISY_DEBUG) log_it("adding newly done: " + (string)new_rezzee); + already_done_items += [ new_rezzee ]; + } + } + } + current_object_index = 0; // reset the current object index. + + return TRUE; +} + +/////////////// + +// returns true if the state should change. +integer handle_timer() +{ + // make sure we run at the fast rate; we'll reset the rate elsewhere if needed. + llSetTimerEvent(BASE_TIMER_PERIOD); + + if (!is_enabled_now) { + if (jaunt_responses_awaited > 0) { + if (SUPER_NOISY_DEBUG) log_it("done rez, but jaunts awaited."); + return FALSE; + } + if (llVecDist(llGetPos(), main_home) > HOME_PROXIMITY) { + if (SUPER_NOISY_DEBUG) log_it("done rez, but not at home."); + attempt_to_go_home(); + return FALSE; + } + if (llGetListLength(gift_stack)) { + if (SUPER_NOISY_DEBUG) log_it("done rez, but still pending gifts."); + return FALSE; + } + if (SUPER_NOISY_DEBUG) log_it("done rez, totally done."); + llSetTimerEvent(0.0); + return TRUE; + } + + // check whether the max run time has elapsed. + integer run_time = llAbs(llGetUnixTime() - when_last_enabled); + if (run_time > max_run_time) { + log_it("Maximum run time elapsed; shutting down."); + is_enabled_now = FALSE; + llSetTimerEvent(0.0); + return TRUE; + } + + // we have not gotten a configuration yet. + if (global_notecard_name == "") { +/// if (SUPER_NOISY_DEBUG) log_it("in timer with no notecard???"); + return FALSE; + } + + // crank to next step in config. + if (!run_through_rez_plan()) { +//log_it("told done with rez plan"); + // we were told not to keep going. if this is a periodic run, + // then we'll snooze until the next time. + if (obj_rezzing_period != 0.0) { + llSetTimerEvent(obj_rezzing_period); + finished_file = FALSE; + } + } + // even if the run were done, we still have to handle termination conditions. + return FALSE; +} + +// processes requests made by avatars via chat. +integer handle_hearing_voices(integer channel, string name, key id, string message) +{ + string rez_command = "#rez"; + // is this our command prefix? + if (is_prefix(message, rez_command)) { + // we found a command. which specific one? + string parm = llDeleteSubString(message, 0, llStringLength(rez_command)); + // eat the command portion, plus a space. + if ( (parm == "on") && !is_enabled_now) { + is_enabled_now = TRUE; + return TRUE; + } else if ( (parm == "off") && is_enabled_now) { + is_enabled_now = FALSE; + return TRUE; + } + } + return FALSE; +} + +// a helper function that turns the word "random" into a random value. +// we will never allow a negative z value for the random amount, since that will often be +// wrong (rezzing underneath where the object lives, e.g.). this implies always +// putting the rezzer below where one wants to see the random rezzing action. +vector maybe_randomize_offset(string vector_or_not, vector largest_add) +{ + if (vector_or_not == "random") { + vector just_xy = ; + vector just_z = <0.0, 0.0, largest_add.z>; + vector to_return = random_bound_vector(<0.0, 0.0, 0.0>, just_xy, TRUE) + + random_bound_vector(<0.0, 0.0, 0.0>, just_z, FALSE); +//log_it("calc rand off: " + (string)to_return); + return to_return; + } else { + return (vector)vector_or_not; + } +} + +vector maybe_randomize_rotation(string vector_or_not, vector largest_add) +{ + if (vector_or_not == "random") { + vector to_return = random_bound_vector(<0.0, 0.0, 0.0>, largest_add, TRUE); +//log_it("calc rand rot: " + (string)to_return); + return to_return; + } else { + return (vector)vector_or_not; + } +} + +// returns a random vector where x,y,z will be between "minimums" and "maximums" +// x,y,z components. if "allow_negative" is true, then any component will +// randomly be negative or positive. +vector random_bound_vector(vector minimums, vector maximums, integer allow_negative) +{ + return ; +} + +// consumes the notecard in a very application specific way +// to retrieve the configurations for the items to rez. +parse_variable_definition(string to_parse) +{ + string content; // filled after finding a variable name. + + if ( (content = get_variable_value(to_parse, "name")) != "") + obj_name = content; + else if ( (content = get_variable_value(to_parse, "position")) != "") + obj_position = (vector)content; + else if ( (content = get_variable_value(to_parse, "offset")) != "") + obj_offset = content; + else if ( (content = get_variable_value(to_parse, "rotation")) != "") + obj_rotation = content; + else if ( (content = get_variable_value(to_parse, "multi_offset")) != "") + obj_multi_offset = content; + else if ( (content = get_variable_value(to_parse, "multi_angle")) != "") + obj_multi_angle = content; + else if ( (content = get_variable_value(to_parse, "singleton")) != "") + obj_is_singleton = (integer)content; + else if ( (content = get_variable_value(to_parse, "count")) != "") + rezzed_at_once = (integer)content; + else if ( (content = get_variable_value(to_parse, "rate")) != "") + obj_rezzing_period = (float)content; + else if ( (content = get_variable_value(to_parse, "max_run")) != "") + max_run_time = (integer)content; + else if ( (content = get_variable_value(to_parse, "gift")) != "") + gift_card_name = content; + else if ( (content = get_variable_value(to_parse, "maxrandoff")) != "") + obj_max_offset_add = (vector)content; + else if ( (content = get_variable_value(to_parse, "maxrandrot")) != "") + obj_max_rotation_add = (vector)content; + else if ( (content = get_variable_value(to_parse, "owner_only")) != "") + owner_only = (integer)content; + else if ( (content = get_variable_value(to_parse, "die_channel")) != "") + DIE_ON_DEMAND_CHANNEL = (integer)content; + else if ( (content = get_variable_value(to_parse, "perform_jaunts")) != "") + PERFORM_JAUNTS = (integer)content; +} + +// examines the configuration to find one section of rezzable plan. +// returns TRUE when the entire config has been consumed. +integer read_one_section() +{ + integer count = llGetListLength(global_config_list); + if (global_config_index >= count) return TRUE; // all done. + integer sec_indy = global_config_index; + string line = llList2String(global_config_list, sec_indy); + // search for a section beginning. + if (llGetSubString(line, 0, 0) == "[") { + // we found the start of a section name. now read the contents. + reset_variables_to_defaults(); // clean out former thoughts. + if (SUPER_NOISY_DEBUG) + log_it("reading section: " + llGetSubString(line, 1, -2)); + // iterate across the items in current config section. + // initializer skips line we just read. + for (sec_indy++; sec_indy < count; sec_indy++) { + // read the lines in the section. + line = llList2String(global_config_list, sec_indy); + if (llGetSubString(line, 0, 0) != "[") { + // try to interpret this line as a variable setting. + parse_variable_definition(line); + } else { + // done chowing on defs, so break out. + global_config_index = sec_indy; + // if the index is past the end, we're all done. + return global_config_index >= count; + } + } + global_config_index = sec_indy; + if (SUPER_NOISY_DEBUG) log_it("read_one_section finished file, post loop."); + return TRUE; // at end of list. + } else { + if ( (line != "") && !is_prefix(line, "#") ) { + if (SUPER_NOISY_DEBUG) log_it("skipping gibberish: " + line); + global_config_index++; + return FALSE; // not done yet. + } + } + + if (SUPER_NOISY_DEBUG) log_it("read_one_section finished file, bottom bailout."); + return TRUE; +} + +// resets our variables to the default parameters. +reset_variables_to_defaults() +{ + obj_name = ""; + obj_offset = "<0.0, 0.0, 0.0>"; + obj_position = <0.0, 0.0, 0.0>; + obj_rotation = "<0.0, 0.0, 0.0>"; + obj_is_singleton = FALSE; + obj_multi_offset = "<0.0, 0.0, 0.0>"; + obj_multi_angle = "<0.0, 0.0, 0.0>"; + rezzed_at_once = 1; + obj_rezzing_period = 0.0; + max_run_time = 1200; + gift_card_name = ""; + owner_only = FALSE; +} + +integer completed_previous_section; // had the last section finished processing? + +integer finished_file = FALSE; // has the config file been consumed? + +// returns TRUE while it should continue to be called at the normal rate. +integer run_through_rez_plan() +{ + if (jaunt_responses_awaited > 0) return TRUE; // not ready yet. + if (sensor_checks_pending > 0) return TRUE; // not ready yet. + + if (completed_previous_section) { +// if (SUPER_NOISY_DEBUG) log_it("decided to read config, prev sec complete now..."); + // read in the next section of config. + finished_file = read_one_section(); + // reset the current counters of rezzed items for the section. + current_count = 0; + current_object_index = 0; + recent_items_rezzed = 0; + already_done_items = []; + } + // now fire off the rez before we start the next section. + integer worked = perform_rezzing_action(); + if (!worked) { + // we can't keep going; there needs to be a pause. +// if (SUPER_NOISY_DEBUG) log_it("still working on previous section."); + completed_previous_section = FALSE; + return TRUE; + } else { +// if (SUPER_NOISY_DEBUG) log_it("completed running previous section."); + completed_previous_section = TRUE; + } + + // check whether we rezzed anything at all, and if we're done. + if (finished_file) { +//log_it("seeing file as finished"); + if (obj_rezzing_period == 0.0) { + // this one is not periodic, so we finish out its cycle. + if (DEBUGGING) log_it("shutting down rezzer; no objects left to create."); + is_enabled_now = FALSE; + } + global_config_index = 0; // reset reading position for config. + attempt_to_go_home(); // try to go home after the run. + // periodic plans will just cause a snooze now. + return FALSE; + } + return TRUE; +} + +// resets all our variables and starts reading the rez plan. +crank_rezzer_up() +{ + is_enabled_now = TRUE; // turn the device on. + + main_home = llGetPos(); // set our home location now. + main_rotate = llGetRot(); // set our home rotation also. + when_last_enabled = llGetUnixTime(); // restart the run counter. + global_config_list = []; // no config yet. + global_singleton_checker = ""; // we are not working on a sensor check for anything. + sensor_checks_pending = 0; // no sensors are awaited. + recent_items_rezzed = 0; // nothing rezzed recently. + jaunt_responses_awaited = 0; // no jumps in progress. + rezzed_overall = 0; // nothing has been rezzed yet. + gift_stack = []; // no gifts waiting yet. + + request_configuration(); // try to find a notecard and read our config. + + // announce that we're open for business. + if (DEBUGGING) log_it("rezzer started... [free mem=" + (string)llGetFreeMemory() + "]"); +} + +////////////// +// from hufflets... +// +integer debug_num = 0; + +// in the case of rezzeroni, we want to say things out loud for anyone +// to hear, since often the script is open to access by anyone. +// when debugging, we want to say to the owner also, so that problems can be removed. + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + if (!SUPER_NOISY_DEBUG) { + // say this on open chat. + llSay(0, "[" + (string)debug_num + "] " + to_say); + } else { + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + } +} + +// locates the string "text" in the list to "search_in". +integer find_in_list(list search_in, string text) +{ + integer len = llGetListLength(search_in); + integer i; + for (i = 0; i < len; i++) { + if (llList2String(search_in, i) == text) + return i; + } + return -1; +} + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return (llSubStringIndex(compare_with, prefix) == 0); } + +// returns a number at most "maximum" and at least "minimum". +// if "allow_negative" is TRUE, then the return may be positive or negative. +float randomize_within_range(float minimum, float maximum, integer allow_negative) +{ + if (minimum > maximum) { + // flip the two if they are reversed. + float temp = minimum; minimum = maximum; maximum = temp; + } + float to_return = minimum + llFrand(maximum - minimum); + if (allow_negative) { + if (llFrand(1.0) < 0.5) to_return *= -1.0; + } + return to_return; +} + +// returns a non-empty string if "to_check" defines a value for "variable_name". +// this must be in the form "X=Y", where X is the variable_name and Y is the value. +string get_variable_value(string to_check, string variable_name) +{ + // clean initial spaces. + while (llGetSubString(to_check, 0, 0) == " ") + to_check = llDeleteSubString(to_check, 0, 0); + if (!is_prefix(to_check, variable_name)) return ""; + to_check = llDeleteSubString(to_check, 0, llStringLength(variable_name) - 1); + // clean any spaces or valid assignment characters. + while ( (llGetSubString(to_check, 0, 0) == " ") + || (llGetSubString(to_check, 0, 0) == "=") + || (llGetSubString(to_check, 0, 0) == ",") ) + to_check = llDeleteSubString(to_check, 0, 0); +//still too noisy... +// if (SUPER_NOISY_DEBUG) +// log_it("set '" + variable_name + "' = '" + to_check + "'"); + string chewed_content = to_check; +//this is patching in CR. shouldn't that be separate? + integer indy; + for (indy = 0; indy < llStringLength(chewed_content); indy++) { + if (llGetSubString(chewed_content, indy, indy) == "\\") { + if (llGetSubString(chewed_content, indy+1, indy+1) == "n") { + chewed_content = llGetSubString(chewed_content, 0, indy - 1) + + "\n" + + llGetSubString(chewed_content, indy + 2, + llStringLength(chewed_content) - 1); + } + } + } + // return what's left of the string. + return chewed_content; +} + +// locates the item with "name_to_find" in the inventory items with the "type". +// a value from 0 to N-1 is returned if it's found, where N is the number of +// items in the inventory. +integer find_in_inventory(string name_to_find, integer inv_type) +{ + integer num_inv = llGetInventoryNumber(inv_type); + if (num_inv == 0) return -1; // nothing there! + integer inv; + for (inv = 0; inv < num_inv; inv++) { + if (llGetInventoryName(inv_type, inv) == name_to_find) + return inv; + } + return -2; // failed to find it. +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +// end hufflets +////////////// + +integer trigger_stop_message = FALSE; + // a simple logging switch; first time in to awaiting commands, + // we do not want to say a run ended. but every time after that, + // we do want to. + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + if (DEBUGGING) log_it(""); + // we immediately jump into the command handling state. + // this state is really only for cleaning up older scripts with the auto-upgrade. + state awaiting_commands; + } +} + +state awaiting_commands +{ + state_entry() { + if (DEBUGGING) log_it(""); + llSetTimerEvent(0.0); + if (trigger_stop_message) + if (DEBUGGING) log_it("rezzer finished. [free mem=" + (string)llGetFreeMemory() + "]"); + trigger_stop_message = TRUE; // always say it next time. + reset_variables_to_defaults(); // clean out any old notes. + llListen(0, "", NULL_KEY, ""); + } + + touch_start(integer count) { + // see if we're blocking others right now. + if (owner_only && (llDetectedKey(0) != llGetOwner())) return; + state reading_rez_plan; + } + + // is someone speaking to the object? + listen(integer channel, string name, key id, string message) { + if (owner_only && (llDetectedKey(0) != llGetOwner())) return; + if (handle_hearing_voices(channel, name, id, message)) state reading_rez_plan; + } +} + +state reading_rez_plan +{ + state_entry() { + if (DEBUGGING) log_it(""); + is_enabled_now = FALSE; + +// // we reset this list only here, since during periodic we'll keep resetting all +// // our other variables. this status must persist between runs though. +// already_done_items = []; // nothing's been done yet. + + // reset our state so the rez plan will start being read. + completed_previous_section = TRUE; + finished_file = FALSE; + + // get the timer cranking so we can process the configuration. + crank_rezzer_up(); + } + + // process the response from the noteworthy library. + link_message(integer which, integer num, string msg, key id) + { + if (handle_link_message(which, num, msg, id)) + state running_rez_plan; + } + + touch_start(integer count) {} // do nothing in this state, but keep touch event alive. + +} + +state running_rez_plan +{ + state_entry() { + if (DEBUGGING) log_it(""); + llListen(0, "", NULL_KEY, ""); + llSetTimerEvent(BASE_TIMER_PERIOD); + } + + state_exit() { llSetTimerEvent(0); } + + // we got clicked. + touch_start(integer count) { + if (owner_only && (llDetectedKey(0) != llGetOwner())) return; + // disable the rezzing process. + is_enabled_now = FALSE; + llSetTimerEvent(BASE_TIMER_PERIOD); + // now we must allow the timer to handle the return process. + } + + // is someone speaking to the object? + listen(integer channel, string name, key id, string message) { + if (owner_only && (llDetectedKey(0) != llGetOwner())) return; + if (handle_hearing_voices(channel, name, id, message)) is_enabled_now = FALSE; + llSetTimerEvent(BASE_TIMER_PERIOD); + } + + timer() { + if (handle_timer()) state awaiting_commands; + } + + object_rez(key id) { + if (SUPER_NOISY_DEBUG) log_it("heard object rez: id=" + (string)id); + if (!llGetListLength(gift_stack)) return; // no gifts to hand out. + string new_gift = llList2String(gift_stack, 0); + gift_stack = llDeleteSubList(gift_stack, 0, 0); + if (find_in_inventory(new_gift, INVENTORY_NOTECARD) < 0) { + // missing the present. this seems remarkable. + log_it("could not find gift specified: " + new_gift); + return; + } + llGiveInventory(id, new_gift); + } + + // process the response from the noteworthy library. + link_message(integer which, integer num, string msg, key id) + { handle_link_message(which, num, msg, id); } + + // a sensor result came in--process either no match or the match. + no_sensor() { global_found_the_singleton = FALSE; sensor_checks_pending--; } + sensor(integer num_detected) { global_found_the_singleton = TRUE; sensor_checks_pending--; } +} diff --git a/huffware/huffotronic_updater_freebie_v5.3/rotanium_rotato_v2.7.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/rotanium_rotato_v2.7.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/rotanium_rotato_v2.7.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/rotanium_rotato_v2.7.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_armature_v15.3.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_armature_v15.3.lsl new file mode 100755 index 00000000..e6d81c39 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_armature_v15.3.lsl @@ -0,0 +1,492 @@ + +// huffware script: searchbert armature, by fred huffhines +// +// this script serves two useful functions for searchbert: +// 1) this is searchbert's ease-of-use auto-repair feature. the script puts searchbert's arms back +// on if they should ever get ripped off. this happens more frequently than one might expect, but this +// script makes searchbert feel all better again. +// 2) this script also manages the exchange of resources between the root prim and the arms. if there +// are updates to the search pointer script or others, they'll get synched on a periodic basis. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +////////////// + +// global constants. + +integer DEBUGGING = FALSE; // set to true to make the script noisier. + +integer SPECIAL_STARTUP_SAUCE = -10408; + // used to signal that this is a real searchbert arm, so it should evaporate on detach. + +integer INVENTORY_UPDATE_INTERVAL = 6000; + // how often to rescan the inventory so sub-prims get the right contents, in seconds. + +integer TOTAL_SEARCH_RODS = 17; // given our current pattern, this is how many we rez. + +// object maintenance variables... + +integer objects_rezzed = 0; // how many kids did we see created? + +integer alerted_brainiac = FALSE; // did we tell the brain that our arms are good? + +////////////// + +// exported interfaces... + +// the armature button pushing API. +// (we have subclassed the simple button pushing API for searchbert armature.) +////////////// +integer BUTTON_PUSHER_HUFFWARE_ID = 10029; + // a unique ID within the huffware system for this script. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +////////////// +string BUTTON_PUSHED_ALERT = "#btnp"; + // this event is generated when the button is pushed. the number parameter will be + // the huffware id plus the reply distance. the id parameter in the link message will + // contain the name of the button that was pushed. +////////////// +string CHECK_ARMS_BUTTON_NAME = "^checkarms"; + // this is the signal given to the armature script that it should check the + // number of arms present on searchbert. if the number is fine, it will + // push the arms are good button back at the brainiac (using the button + // push api plus reply distance). +string ARMS_ARE_GOOD_BUTTON_NAME = "^goodarmz"; + // the event sent back by the searchbert armature when all arms are ready + // to go. +string PROBLEM_WITH_MY_THUMBS_BUTTON_NAME = "^ouch"; + // a problem was noticed with the number of arms and we could not fix it. + // the brain needs to try reconfiguring again. +////////////// + +// imported interfaces... + +// inventory exchanger API. +////////////// +// do not redefine these constants. +integer INVENTORY_EXCHANGER_HUFFWARE_ID = 10021; + // the unique id within the huffware system for the jaunt script to + // accept commands on. this is used in llMessageLinked as the num parameter. +//string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +//string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +//integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +////////////// +// commands available via the library: +string REGISTER_ASSETS_COMMAND = "#regis#"; + // makes the root prim's inventory exchanger track a set of items which each participating + // child prim should have. this command is only available to the root prim; child prims + // cannot register any assets but instead can get updates on the assets. the parameter + // required is a list of asset definitions, wrapped by the huffware item separator. each + // asset definition is in turn a two part list wrapped with the asset field separator. +string ASSET_FIELD_SEPARATOR = "&&"; // separates the type from the name in our list. +string WILDCARD_INVENTORY_NAME = "ALL"; + // this keyword can be used to register all of the inventory items available of the + // specified type. this should be passed as the inventory item name in the second half of + // an asset definition list. +string WHACK_INVENTORY_SIGNAL = "DEL*"; + // ensures that the child prim doesn't maintain *any* of the inventory of the type specified. +string AVAILABLE_ASSETS_EVENT = "#gotz#"; + // the root prim publishes this event to list out what items it has available. the child + // prims need to ask for anything that they're currently missing. the included parameters + // are in the same format as the register assets command, but no wildcards will be present; + // all asset names will be fully expanded. +string REQUEST_UPDATES = "#nupd#"; + // used by the child prim to request an updated version of an asset or assets. the parameters + // should follow the register assets command. +string FINISHED_UPDATING = "#donq#"; + // a signal sent from the root prim to the child prim when the root has finished updating + // the assets requested. this lets the child know that it can clean out any outdated versions + // of items which got an update. there are no parameters to this, because the child prim + // should have at most one update outstanding at a time. +////////////// + +// startup function that gets things going. this is not called at startup time, it +// has to be activated by the brain script. +initialize_armatures() +{ + objects_rezzed = 0; + alerted_brainiac = FALSE; + check_integrity(); +} + +// starts the process of exchanging the inventory items with our arms. +initialize_exchanger() +{ + llSetTimerEvent(5); // check on inventory soon. +} + +// looks for an inventory item with the same prefix as the "basename_to_seek". +integer find_basename_in_inventory(string basename_to_seek, integer inv_type) +{ + integer num_inv = llGetInventoryNumber(inv_type); + if (num_inv == 0) return -1; // nothing there! + integer indy; + for (indy = 0; indy < num_inv; indy++) { + if (is_prefix(llGetInventoryName(inv_type, indy), basename_to_seek)) + return indy; + } + return -2; // failed to find it. +} + +// lets the searchbert brain know the arms are all ready. +report_arms_are_good() +{ + if (!alerted_brainiac) { + // let the brain know we're doing okay on our arm count. + alerted_brainiac = TRUE; + llMessageLinked(LINK_SET, BUTTON_PUSHER_HUFFWARE_ID + REPLY_DISTANCE, + BUTTON_PUSHED_ALERT, ARMS_ARE_GOOD_BUTTON_NAME); + } +} + +// we ran into a snag, like the user didn't give us permission. we need the +// controlling script to try this all again. +report_arms_are_bad() +{ + alerted_brainiac = FALSE; + llSay(0, "I did not get permission to attach my seeker arms."); + llMessageLinked(LINK_SET, BUTTON_PUSHER_HUFFWARE_ID + REPLY_DISTANCE, + BUTTON_PUSHED_ALERT, PROBLEM_WITH_MY_THUMBS_BUTTON_NAME); +} + +// returns the name of the inventory exchange script, if one is present. we don't worry +// about there being more than one present; that's a different script's problem. +list get_exchanger_and_pointer_names() +{ + list to_return; + integer found = find_basename_in_inventory("inventory exchanger", INVENTORY_SCRIPT); + if (found >= 0) { + to_return += [ (string)INVENTORY_SCRIPT + ASSET_FIELD_SEPARATOR + + llGetInventoryName(INVENTORY_SCRIPT, found) ]; + } + found = find_basename_in_inventory("huff-search pointer", INVENTORY_SCRIPT); + if (found >= 0) { + to_return += [ (string)INVENTORY_SCRIPT + ASSET_FIELD_SEPARATOR + + llGetInventoryName(INVENTORY_SCRIPT, found) ]; + } + return to_return; +} + +// tell the root prim's inventory exchanger what to monitor. +post_exchangeable_assets() +{ + // it's time to post. we allow a couple seconds leeways since timers are inexact. + llMessageLinked(LINK_THIS, INVENTORY_EXCHANGER_HUFFWARE_ID, REGISTER_ASSETS_COMMAND, + // what we really want here is to make sure our pointer script is kept up to date. + wrap_parameters(get_exchanger_and_pointer_names() )); +} + +// our timer handling function; this will move the search engine to the next place on +// the current spiral (or to the start of the next spiral) when it is called. +// if we're not currently running a search, then it will just periodically update the +// sub-prims to make sure they have the latest versions. +handle_timer() +{ + // stop the clock. + llSetTimerEvent(0); + // if we haven't updated the inventory assets in a while, then we want + // to make sure the sub-prims have all the right versions of things. + post_exchangeable_assets(); + // restart ticking. + llSetTimerEvent(INVENTORY_UPDATE_INTERVAL); +} + +// this function is called when we have acquired permissions to manipulate this object, +// so we can crank up all the search rods to be our seekers. +got_link_permissions() +{ + if ( (llGetNumberOfPrims() - 1) >= TOTAL_SEARCH_RODS) { + // seems like the arms are already present. + objects_rezzed = TOTAL_SEARCH_RODS; + if (DEBUGGING) log_it("I'm done setting up my arms."); + // make sure we alert brain. + check_integrity(); + return; + } + + // reset our count now that we're going to reattach everything. + objects_rezzed = 0; + + // postpone the next inventory exchange until we have all our arms on. + // each link addition takes at least a second of built in delay (currently). + llSetTimerEvent(TOTAL_SEARCH_RODS + 7); + + llBreakAllLinks(); // drop all current attachments and let them evaporate. + + string search_object = llGetInventoryName(INVENTORY_OBJECT, 0); + + integer which_locator = 2; + float curr_rotation; + // calculate the size of the root prim so we can put the search arms in the + // right locations. + list sizes = llGetBoundingBox(llGetKey()); + vector min = llList2Vector(sizes, 0); + vector max = llList2Vector(sizes, 1); + // not exactly sure if this formula will always be right. have seen some anomalous + // results from get bounding box. + float radius = (max.x - min.x) / 2.0; + + for (curr_rotation = 0.0; curr_rotation < PI / 4.0 + .001; + curr_rotation += PI / 4.0) { + float x = 0.0; + // handle dots on x axis. + for (x = -0.4 / 0.54 * radius; x < 0.41 / 0.54 * radius; x += 0.2 / 0.54 * radius) { + if ( (curr_rotation != 0.0) || (llRound(x * 100.0) != 0) ) { + vector rez_place = ; + rotation z_45 = llEuler2Rot(<0.0, 0.0, curr_rotation>); + rez_place *= z_45; + rez_place += llGetPos(); +//log_it("x rez place " + (string)rez_place + " rot=" + (string)curr_rotation); + which_locator++; + llRezObject(search_object, rez_place, ZERO_VECTOR, ZERO_ROTATION, + SPECIAL_STARTUP_SAUCE); + } + } + float y = 0.0; + // handle dots on y axis. + for (y = -0.4 / 0.54 * radius; y < 0.41 / 0.54 * radius; y += 0.2 / 0.54 * radius) { + if (llRound(y * 100.0) != 0) { + vector rez_place = <0.0, y, 0.0>; + rotation z_45 = llEuler2Rot(<0.0, 0.0, curr_rotation>); + rez_place *= z_45; + rez_place += llGetPos(); +//log_it("y rez place " + (string)rez_place + " rot=" + (string)curr_rotation); + which_locator++; + llRezObject(search_object, rez_place, ZERO_VECTOR, ZERO_ROTATION, + SPECIAL_STARTUP_SAUCE); + } + } + } +} + +handle_new_child(key new_child) +{ + // report (via text label) how many others there are still to hook in. + integer count; + integer remaining_to_rez = TOTAL_SEARCH_RODS - objects_rezzed; + string tag; + for (count = 0; count < remaining_to_rez; count++) tag += "+"; + llSetText(tag + " " + (string)remaining_to_rez + " " + tag, + <0.4 + llFrand(0.6), 0.4 + llFrand(0.6), 0.4 + llFrand(0.6)>, 1.0); + // link the new kid to us. + // this also delays the script a whole second?! argh. + llCreateLink(new_child, TRUE); + // check if we're done yet. + if (++objects_rezzed >= TOTAL_SEARCH_RODS) { + // that was the last one. + llSetText("", <0.0, 0.0, 0.0>, 0.0); + if (DEBUGGING) log_it("All of my arms have been rezzed and connected now."); + // one last check and alert the brain about us being ready. + check_integrity(); + } +} + +// processes a request for our armature services, probably from the search brain. +handle_link_message(integer which, integer num, string msg, key id) +{ + if ( (num == BUTTON_PUSHER_HUFFWARE_ID) + && (msg == BUTTON_PUSHED_ALERT) + && (id == CHECK_ARMS_BUTTON_NAME) ) { + // see if we can put our arms on, if they're not already. + initialize_armatures(); + } +} + +// makes sure that searchbert is all assembled properly. +check_integrity() +{ + if (llGetNumberOfPrims() - 1 < TOTAL_SEARCH_RODS) { + alerted_brainiac = FALSE; + if (DEBUGGING) log_it("I do not have enough arms on my body. Please grant me permission to link and delink."); + // get permissions to take off our arms and add possibly updated ones. + llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS); + } else { + if (!alerted_brainiac) { + // let the brain know we're doing okay on our arm count. + report_arms_are_good(); + alerted_brainiac = TRUE; + } + } +} + +////////////// +// from hufflets... + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +integer debug_num = 0; +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llWhisper(0, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on an unusual channel for chat if it's not intended for general public. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat that anyone can hear. we take off the bling for this one. +// llSay(0, to_say); +} + +// joins a list of parameters using the parameter sentinel for the library. +string wrap_parameters(list to_flatten) +{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); } + +string wrap_item_list(list to_wrap) +{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); } + +// returns the index of the first occurrence of "pattern" inside +// the "full_string". if it is not found, then a negative number is returned. +integer find_substring(string full_string, string pattern) +{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); } + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return find_substring(compare_with, prefix) == 0; } + +// end hufflets. +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default { + state_entry() { + auto_retire(); + initialize_exchanger(); + } + + on_rez(integer parm) { llResetScript(); } + + object_rez(key new_child) { handle_new_child(new_child); } + + timer() { handle_timer(); } + + link_message(integer which, integer num, string str, key id) + { handle_link_message(which, num, str, id); } + + run_time_permissions(integer perm) { + if (perm & PERMISSION_CHANGE_LINKS) + got_link_permissions(); + else + report_arms_are_bad(); + } + + changed(integer change) { + if (change & CHANGED_INVENTORY) { + llSetTimerEvent(2); // fire exchange sooner in case scripts changed. + } else if (change & CHANGED_LINK) { + check_integrity(); + } + } + +} + diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_config_v0.4.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_config_v0.4.lsl new file mode 100755 index 00000000..e86e2d86 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_config_v0.4.lsl @@ -0,0 +1,22 @@ +#searchbert + +# see the huff-search brainiac script for more information about what these mean. they correspond to the +# global variables that are "useful to change" near the top of the script. + +# can only point to maximum of 17 things. +max_matches = 17 +huff_search_chat_channel = 0 +traverse_lands = 0 + +sensor_max_range = 96.0 +# angular default is pi / 2 (90 degrees). +search_rod_angular_sweep = 1.57079632679 + +max_spiral_radius = 72.0 +spiral_loops = 7 +total_steps = 42 +max_spiral_traversals = 1 + +travelling_upwards = 0 +max_upwards_distance = 30.0 + diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_menus_v4.6.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_menus_v4.6.lsl new file mode 100755 index 00000000..719813c2 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/searchbert_menus_v4.6.lsl @@ -0,0 +1,505 @@ + +// huffware script: searchbert menus, by fred huffhines. +// +// manages the menu system for searchbert so the main script is free to do its thing. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +//notes +// + need to have the searchbert initiate anything here. this cannot be free range and +// running even if the searchbert isn't in a menuable mode. +// + is the above true? + +// global constants... + +string MAIN_MENU_NAME = "main"; // name we use in menu list manager for the main menu. +// boilerplate text that's shown at top of main menu. we'll add the channel info later. +string MAIN_MENU_PREFIX = "Quick help: Say '"; +string MAIN_MENU_MIDDLE = "#find XYZ' in chat to locate nearby objects with 'XYZ' in their names.\nSay '"; +string MAIN_MENU_SUFFIX = "#reset' to stop showing a previous search.\n\n[Matches] shows recent search results.\n[Configure] changes options.\n[Reset] clears recent results.\n[Help] dispenses an instruction notecard."; + +// main menu items... +string HELP_CHOICE = "Help"; +string CONFIG_CHOICE = "Configure"; +string MATCHES_CHOICE = "Matches"; +string RESET_CHOICE = "Reset"; + +// configuration menu items... +string MAX_MATCHES_CHOICE = "Max Match"; +string CHAT_CHANNEL_CHOICE = "Channel"; + +// menu indices... +integer MAIN_MENU_INDEX = 0; // we always do main menu as zero. +integer CONFIG_MENU_INDEX = 1; // choices for configurable items. +integer MAX_MATCHES_MENU_INDEX = 2; // changing the number of matches. + +// searchbert menus API. +////////////// +// do not redefine these constants. +integer SEARCHBERT_MENUS_HUFFWARE_ID = 10034; + // the unique id within the huffware system for this script. +////////////// +string SM_CONFIGURE_INFO = "#sm-info#"; + // sets important information this script will use, such as (1) the channel for listening. +string SM_POP_MAIN_MENU_UP = "#sm-main#"; + // causes the main menu to be displayed. this requires an avatar name and avatar key for the + // target of the menu. +////////////// +string SM_EVENT_MENU_CLICK = "#sm-clik#"; + // the user has requested a particular menu item that this script cannot fulfill. the + // event is generated back to the client of this script for handling. it will include (1) the + // menu name in question, (2) the item clicked, (3) the avatar name, and (4) the avatar key. +////////////// + +// imported interfaces... + +// menu list manager link message API. +////////////// +// do not redefine these constants. +integer MENU_LIST_MANAGER_HUFFWARE_ID = 10033; + // the unique id within the huffware system for this sensor plugin script. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +string MENU_LIST_MGR_RESET = "#ml-reset#"; + // throws out any current menus and gets ready to load a new set. +string MENU_LIST_MGR_ADD_MENU = "#ml-addmenu#"; + // puts a new menu into our list. this requires 3 parameters: menu name, + // menu title, menu button list. +string MENU_LIST_MGR_SHOW_MENU = "#ml-shomenu#"; + // a request that a particular menu be shown. the first parameter is the menu index. + // the second parameter is the avatar name. the third parameter is the avatar key. +string MENU_LIST_MGR_GIVE_ITEM = "#ml-give#"; + // brings up a gift giving menu. parms are avatar name and avatar key. +string MENU_LIST_MGR_MODIFY_MENU = "#ml-modmenu#"; +string MENU_LIST_KEEP_WORD = "KEEP"; + // replaces a menu already in the list. this requires 4 parameters: menu index, menu name, + // menu title, menu button list. if a parameter is the MENU_LIST_KEEP_WORD, then that field + // is not changed. +string MENU_LIST_MGR_CHOICE_MADE_ALERT = "#ml-event-picked"; + // alerts the driver for this script that the owner has picked a choice. the + // parameters include: the text of the choice, the name of the menu, the avatar name, + // the avatar key. +// +////////////// + +// we snag just enough of this interface to masquerade as it... +// card configurator link message API: +////////////// +// do not redefine these constants. +integer CARD_CONFIGURATOR_HUFFWARE_ID = 10042; + // the unique id within the huffware system for the card configurator script to + // accept commands on. this is used in llMessageLinked as the num parameter. +////////////// +string CARD_CONFIG_RECEIVED_ALERT = "#cfg-event-upd#"; + // this message is sent when the configurator has found some data updates or has finished + // reading the configuration file. +////////////// + +// global variables... + +integer initialized_menus_yet = FALSE; + // records if the menus have been set up. + +// additional information that's provided by the client script. +integer TALKY_CHANNEL = 0; + +////////////// + +// sends a new menu to be placed in the menu manager. +add_menu(string menu_name, string menu_title, list button_list) +{ + if (llGetListLength(button_list) == 0) { + // patch this degenerate list into a bogus one. + button_list = [ "none" ]; + } + llMessageLinked(LINK_ROOT, MENU_LIST_MANAGER_HUFFWARE_ID, MENU_LIST_MGR_ADD_MENU, + menu_name + HUFFWARE_PARM_SEPARATOR + + menu_title + HUFFWARE_PARM_SEPARATOR + + wrap_item_list(button_list)); +} + +// stuffs in a new version of an old menu. +replace_menu(integer index, string menu_name, string menu_title, list button_list) +{ + llMessageLinked(LINK_ROOT, MENU_LIST_MANAGER_HUFFWARE_ID, MENU_LIST_MGR_MODIFY_MENU, + (string)index + HUFFWARE_PARM_SEPARATOR + + menu_name + HUFFWARE_PARM_SEPARATOR + + menu_title + HUFFWARE_PARM_SEPARATOR + + wrap_item_list(button_list)); +} + +// sets up the main menu options. if "new_now" is false, then this replaces +// the main menu rather than expecting to add a new one. +establish_main_menu(integer new_now) +{ + // menu zero: this is the main menu. + string menu_name = "main"; + string menu_title = MAIN_MENU_PREFIX + channel_string() + MAIN_MENU_MIDDLE + + channel_string() + MAIN_MENU_SUFFIX; + list menu_button_list = [ MATCHES_CHOICE, CONFIG_CHOICE, RESET_CHOICE, HELP_CHOICE ]; + if (new_now) { + add_menu(menu_name, menu_title, menu_button_list); + } else { + replace_menu(MAIN_MENU_INDEX, menu_name, menu_title, menu_button_list); + } +} + +// sets up all of the static menus for searchbert. +prepare_all_menus() +{ + establish_main_menu(TRUE); // set up the main menu as a new menu. + + // configuration menu provides tasty options for changing the way searchbert behaves. + string menu_name = CONFIG_CHOICE; // reusing menu button from main menu for the menu name. + string menu_title = "Configurable Options...\n[" + MAX_MATCHES_CHOICE + "] selects number of items to find.\n[" + CHAT_CHANNEL_CHOICE + "] changes listening channel for commands.\n"; + list menu_button_list = [ MAX_MATCHES_CHOICE, CHAT_CHANNEL_CHOICE ]; + add_menu(menu_name, menu_title, menu_button_list); + + menu_name = MAX_MATCHES_CHOICE; // reusing menu button from config menu. + menu_title = "Choose a new maximum number of items to find:"; + menu_button_list = produce_nums(17); // ultimate number of arms available (magic constant!). + add_menu(menu_name, menu_title, menu_button_list); + +} + +// if we haven't initialized yet, we'll do it now. +maybe_really_setup_menus() +{ + if (!initialized_menus_yet) { +//log_it("needed initialization still!"); + prepare_all_menus(); + initialized_menus_yet = TRUE; + llSetTimerEvent(0); // stop timer. + return; + } +} + +// deals with the timer elapsing. +handle_timer_hit() +{ + // see if this timer is for initialization purposes. + maybe_really_setup_menus(); +} + +// processes a message requesting our services or updating our info. or ignore it. +handle_link_message(integer sender, integer num, string msg, key id) +{ + if ( (num != MENU_LIST_MANAGER_HUFFWARE_ID + REPLY_DISTANCE) + && (num != SEARCHBERT_MENUS_HUFFWARE_ID) ) + return; + + // make sure we're already initialized. + maybe_really_setup_menus(); + + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + + if (num == MENU_LIST_MANAGER_HUFFWARE_ID + REPLY_DISTANCE) { + if (msg == MENU_LIST_MGR_CHOICE_MADE_ALERT) { + // now deal with the implications of the menu they chose. + process_menu_choice(llList2String(parms, 1), llList2String(parms, 2), + llList2String(parms, 3), llList2String(parms, 0)); + } + return; + } + + // if we got to here, it must be for our main interface methods. +//log_it("got searchbert menu request: " + msg + ". memory left=" + (string)llGetFreeMemory()); + if (msg == SM_CONFIGURE_INFO) { + TALKY_CHANNEL = (integer)llList2String(parms, 0); + establish_main_menu(FALSE); + } else if (msg == SM_POP_MAIN_MENU_UP) { + // show the main menu for the specified avatar. + request_menu_popup(MAIN_MENU_INDEX, llList2String(parms, 0), llList2String(parms, 1)); + } + +} + +request_menu_popup(integer menu_index, string av_name, string av_key) +{ + llMessageLinked(LINK_ROOT, MENU_LIST_MANAGER_HUFFWARE_ID, MENU_LIST_MGR_SHOW_MENU, + wrap_parameters([menu_index, av_name, av_key])); +//log_it("sent menu popup... memory left=" + (string)llGetFreeMemory()); +} + +// generates a list of numbers up to and including the "max". +list produce_nums(integer max) +{ + list to_return; + integer indy; + for (indy = 1; indy <= max; indy++) { + to_return += (string)indy; + } + return to_return; +} + +// returns the appropriate extra text if the channel is not zero for open chat. +string channel_string() +{ + string add_in_channel = ""; + if (TALKY_CHANNEL != 0) + add_in_channel = "/" + (string)TALKY_CHANNEL + " "; + return add_in_channel; +} + +// handles when a menu has been clicked on. +process_menu_choice(string menu_name, string av_name, string av_key, string which_choice) +{ +//log_it("into process menu " + menu_name + " for " + av_name + " who chose " + which_choice); + if (menu_name == MAIN_MENU_NAME) { + if (which_choice == HELP_CHOICE) { + // see if we can find a helper notecard. + integer indy = find_basename_in_inventory("docs", INVENTORY_NOTECARD); + if (indy < 0) { + llSay(0, "We're very sorry, but there do not seem to be any documentation notecards available. There may be a better version at eepaw shop (osgrid or SL)."); + } else { + string invname = llGetInventoryName(INVENTORY_NOTECARD, indy); + llGiveInventory(av_key, invname); + llWhisper(0, "Here's a copy of the help file: " + invname); + } + return; + } else if (which_choice == CONFIG_CHOICE) { + request_menu_popup(CONFIG_MENU_INDEX, av_name, av_key); + return; + } + // MATCHES_CHOICE falls through for forwarding. + // so does RESET_CHOICE. + } else if (menu_name == CONFIG_CHOICE) { + if (which_choice == MAX_MATCHES_CHOICE) { + // let them pick a new maximum number of matches to find. + request_menu_popup(MAX_MATCHES_MENU_INDEX, av_name, av_key); + return; + } else if (which_choice == CHAT_CHANNEL_CHOICE) { + llSay(0, "To change the chat channel, say '" + channel_string() + + "#channel N' where N is your new channel number."); + return; + } +//more config options... + + } else if (menu_name == MAX_MATCHES_CHOICE) { + integer max = (integer)which_choice; + if (max >= 1) { + // a new number of search matches has been decided upon. + send_data_burst(["max_matches", which_choice]); + llSay(0, "Maximum number of matches is now set to " + (string)max + "."); + return; + } + } + + // cases that aren't handled get forwarded to the client script. + llMessageLinked(LINK_ROOT, SEARCHBERT_MENUS_HUFFWARE_ID + REPLY_DISTANCE, SM_EVENT_MENU_CLICK , + wrap_parameters([menu_name, which_choice, av_name, av_key])); +} + +// borrowed from card configurator. +// sends the currently held data out to whoever requested it. +send_data_burst(list to_send) +{ +//log_it("sending " + (string)llGetListLength(to_send) + " items"); + llMessageLinked(LINK_THIS, CARD_CONFIGURATOR_HUFFWARE_ID + REPLY_DISTANCE, CARD_CONFIG_RECEIVED_ALERT, + wrap_parameters(["yo-updated"] + to_send)); + to_send = []; // reset any items held. +} + +// set up all the parts of the externally configured bits. +initialize() +{ +//log_it("initializing. memory left=" + (string)llGetFreeMemory()); + llSleep(0.1); // initial pause before chatting with menu manager. + + // reset the menu manager to get it ready for our new menus. + llMessageLinked(LINK_THIS, MENU_LIST_MANAGER_HUFFWARE_ID, MENU_LIST_MGR_RESET, ""); + + // snooze a bit to allow our helper to wake up. + llSetTimerEvent(0.34); // snooze until helper is ready. + initialized_menus_yet = FALSE; +} + +////////////// +// from hufflets... +// +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat, but use an unusual channel. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); +} + +////////////// + +// joins a list of parameters using the parameter sentinel for the library. +string wrap_parameters(list to_flatten) +{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); } + +string wrap_item_list(list to_wrap) +{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); } + +////////////// + +// locates the string "text" in the list to "search_in". +integer find_in_list(list search_in, string text) +{ + integer len = llGetListLength(search_in); + integer i; + for (i = 0; i < len; i++) { + if (llList2String(search_in, i) == text) + return i; + } + return -1; +} + +// returns the index of the first occurrence of "pattern" inside +// the "full_string". if it is not found, then a negative number is returned. +integer find_substring(string full_string, string pattern) +{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); } + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return find_substring(compare_with, prefix) == 0; } + +// looks for an inventory item with the same prefix as the "basename_to_seek". +integer find_basename_in_inventory(string basename_to_seek, integer inv_type) +{ + integer num_inv = llGetInventoryNumber(inv_type); + if (num_inv == 0) return -1; // nothing there! + integer indy; + for (indy = 0; indy < num_inv; indy++) { + if (is_prefix(llGetInventoryName(inv_type, indy), basename_to_seek)) + return indy; + } + return -2; // failed to find it. +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.8. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +// end hufflets. +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + initialize(); + } + + on_rez(integer parm) { llResetScript(); } + + timer() { handle_timer_hit(); } + + link_message(integer sender, integer num, string msg, key id) { + handle_link_message(sender, num, msg, id); + } + +} + diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/set_comparator_library_v4.1.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/set_comparator_library_v4.1.lsl new file mode 100755 index 00000000..b6d06e24 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/set_comparator_library_v4.1.lsl @@ -0,0 +1,554 @@ + +// huffware script: set comparator, by fred huffhines. +// +// provides a library of functions for managing sets. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// API for set operations. +////////////// +integer SET_COMPARATOR_HUFFWARE_ID = 10020; + // a unique ID within the huffware system for this script. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +////////////// +string DEFINE_SET_CMD = "#def-set"; + // adds a new set or replaces existing one with same name. first parm is name of set, + // second and further parms are a list of elements that should be in the set. return + // value is a boolean for success. +string REMOVE_SET_CMD = "#rm-set"; + // trashes a named set. first parm is the name. returns a bool for success. +string ADD_ELEMENTS_CMD = "#add-elem"; + // adds more elements to an existing set. first parm is the name, second and more are + // the list of new elements. set must already exist. returns a bool for success. +string CUT_ELEMENTS_CMD = "#cut-elem"; + // removes a set of elements from an existing set. first parm is set name, second etc + // is list of elements to remove. set must already exist. returns bool. +string INTERSECT_CMD = "#inter-set"; + // reports the set of elements in the intersection of two sets. first and second parm + // are the set names. returns a wrapped list of elements that are common members of both sets. +string UNION_CMD = "#union-set"; + // returns the union of two named sets. results sent similar to intersection. +string DIFFERENCE_CMD = "#diff-set"; + // returns the difference of set A (parm 1) minus set B (parm 2). results are similar + // to intersection. +string WHAT_MU_CMD = "#mu-set"; + // returns one of the possibility values below to describe the relationship between + // two sets. +string GET_SET_CMD = "#get-set"; + // retrieves the contents of the set named in first parameter. +string LIST_SET_NAMES_CMD = "#whichunz"; + // retrieves the list of set names that exist. +string CLEAR_ALL_CMD = "#clearall"; + // throws out all set definitions. +////////////// +// joins a list of parameters using the parameter sentinel for the library. +string wrap_parameters(list to_flatten) +{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); } +////////////// + +// these are the possible types of set relationships. +string POSSIBILITY_ONE_MEANING = "one meaning (don gcig)"; // same set. +string POSSIBILITY_THREE_POSSES = "proper subset (mu gsum)"; // set A contains set B or vice-versa. +string POSSIBILITY_FOUR_POSSES = "four zones (mu bzhi)"; // non-null intersection, mutual non-null differences. +string POSSIBILITY_MUTUAL_EXCLUDE = "mutually exclusive ('gal ba)"; // no members are shared between the sets. +string POSSIBILITY_ERRONEOUS = "erroneous"; // not a valid request. + +// global variables... + +list all_sets; // a list of condensed lists. +list set_names; // the names of each list in our list. + +integer DEBUGGING = TRUE; // produces noisy logging if true. + +// finds index of named list. +integer find_set(string name_to_find) +{ + return find_in_list(set_names, name_to_find); +} + +// ensures that the list does not contain duplicate members. +list uniquify(list to_strain) { + integer i; + integer j; + for (i = 1; i < llGetListLength(to_strain); i++) { + string curr_i = llList2String(to_strain, i); + for (j = 0; j < i; j++) { + if (llList2String(to_strain, j) == curr_i) { + // this one is a duplicate! argh, remove it at index i. + to_strain = chop_list(to_strain, 0, i - 1) + + chop_list(to_strain, i + 1, llGetListLength(to_strain) - 1); + i--; // skip back since that element no longer exists. + j = i*3; // scoot j out to stop inner loop. + } + } + } + return to_strain; +} + +// this version retrieves bare sets, not ones that are fluffed out from inclusions. +list simple_get_set_by_index(integer index) +{ + if ( (index >= llGetListLength(set_names)) || (index < 0) ) return []; // out of range. + return llParseString2List(llList2String(all_sets, index), [HUFFWARE_ITEM_SEPARATOR], []); +} + + +// adds or replaces the set that is called "name". +integer define_set(string name, list contents) +{ + contents = uniquify(contents); + integer curr = find_set(name); + if (curr >= 0) { + // an existing entry should be updated. + remove_set_by_index(curr); + } + all_sets += [ wrap_item_list(contents) ]; + set_names += [ name ]; +//hmmm: may want to bomb out with false if too much space in use. + return TRUE; +} + +// turns a combination of set linkages into the full set requested. +// assumes that the index is pre-validated! +list evaluate_set_components(integer index) +{ + string wrap = llList2String(all_sets, index); + list to_return = llParseString2List(wrap, [HUFFWARE_ITEM_SEPARATOR], []); + // check for our "equal to set X" case. + if (llGetListLength(to_return) == 1) { + string memb = llList2String(to_return, 0); + if (llGetSubString(memb, 0, 0) == "=") { +//log_it("got a set assignment to other set called: " + llGetSubString(memb, 1, -1)); + // we found a set that's equal to another. look up its equivalent. + index = find_set(llGetSubString(memb, 1, -1)); + if (index < 0) return []; // unknown. + // got another set to check. + wrap = ""; to_return = []; + return evaluate_set_components(index); + } + } + if (llGetListLength(to_return) > 0) { + // scan for set includers. + integer i; + // must iterate forwards, since included sets can also include other sets. + for (i = 0; i < llGetListLength(to_return); i++) { + string curr = llList2String(to_return, i); + if (llGetSubString(curr, 0, 0) == "+") { + // found that this set includes another one. add in the second set, + // but chop out the element we just looked at. + to_return = chop_list(to_return, 0, i - 1) + + chop_list(to_return, i + 1, llGetListLength(to_return) - 1) + + simple_get_set_by_index(find_set(llGetSubString(curr, 1, -1))); + i--; // skip back so we don't omit the new element at i. + } + } + } + return to_return; +} + +// retrieves the specified set from the list using its index. +list get_set_by_index(integer index) +{ + if ( (index >= llGetListLength(set_names)) || (index < 0) ) return []; // out of range. + return evaluate_set_components(index); +} + +// retrieves the specified set from the list using its name. +list get_set_by_name(string set_name) { return get_set_by_index(find_set(set_name)); } + +// drops the set listed at the "index". +integer remove_set_by_index(integer index) +{ + if ( (index >= llGetListLength(set_names)) || (index < 0) ) return FALSE; // out of range. + set_names = llDeleteSubList(set_names, index, index); + all_sets = llDeleteSubList(all_sets, index, index); + return TRUE; +} + +// tosses the set called "set_name". +integer remove_set_by_name(string set_name) { return remove_set_by_index(find_set(set_name)); } + +integer add_items(string name, list new_items) +{ + new_items = uniquify(new_items); + integer curr = find_set(name); + if (curr < 0) return FALSE; // unknown set. + list content = get_set_by_index(curr); + integer i; + for (i = 0; i < llGetListLength(new_items); i++) { + string new_item = llList2String(new_items, i); + integer found = find_in_list(content, new_item); + if (found < 0) { + // was not present yet so add it. + content += [ new_item ]; + } + } + define_set(name, content); + return TRUE; +} + +integer remove_items(string name, list dead_items) +{ + integer curr = find_set(name); + if (curr < 0) return FALSE; // unknown set. + list content = get_set_by_index(curr); + integer i; + for (i = 0; i < llGetListLength(dead_items); i++) { + string dead_item = llList2String(dead_items, i); + integer found = find_in_list(content, dead_item); + if (found >= 0) { + // was not present yet so add it. + content = llDeleteSubList(content, found, found); + } + } + + define_set(name, content); + return TRUE; +} + +// returns the union of two named sets. +list set_union(string set_a, string set_b) +{ + integer where_a = find_set(set_a); + if (where_a < 0) return []; // not known. + integer where_b = find_set(set_b); + if (where_b < 0) return []; + list to_return = get_set_by_index(where_a); + list adding = get_set_by_index(where_b); + integer i; + for (i = 0; i < llGetListLength(adding); i++) { + string curr = llList2String(adding, i); + integer where = find_in_list(to_return, curr); + if (where < 0) { + // wasn't found in to_return, so add it. + to_return += [ curr ]; + } + } + return to_return; +} + +// returns the difference of two named sets. +list set_difference(string set_a, string set_b) +{ + integer where_a = find_set(set_a); + if (where_a < 0) return []; // not known. + integer where_b = find_set(set_b); + if (where_b < 0) return []; + list to_return = get_set_by_index(where_a); + list adding = get_set_by_index(where_b); + integer i; + for (i = 0; i < llGetListLength(adding); i++) { + string curr = llList2String(adding, i); + integer where = find_in_list(to_return, curr); + if (where >= 0) { + // this item does exist in both, so remove it. + to_return = llDeleteSubList(to_return, where, where); + } + } + return to_return; +} + +// returns the intersection of two named sets. +list set_intersection(string set_a, string set_b) +{ + integer where_a = find_set(set_a); + if (where_a < 0) return []; // not known. + integer where_b = find_set(set_b); + if (where_b < 0) return []; + list to_return = []; + list a_con = get_set_by_index(where_a); + list b_con = get_set_by_index(where_b); + integer i; + for (i = 0; i < llGetListLength(b_con); i++) { + string curr = llList2String(b_con, i); + integer where = find_in_list(a_con, curr); + if (where >= 0) { + // this item does exist in both, so include in the result. + to_return += [ curr ]; + } + } + return to_return; +} + +// returns TRUE if minor is a proper subset of major. +integer proper_subset(string minor_set, string major_set) +{ + integer where_a = find_set(minor_set); + if (where_a < 0) return FALSE; // not known. + integer where_b = find_set(major_set); + if (where_b < 0) return FALSE; + list min_con = get_set_by_index(where_a); + list maj_con = get_set_by_index(where_b); + // the list must be smaller to be a proper subset. + if (llGetListLength(min_con) >= llGetListLength(maj_con)) return FALSE; + // now make sure anything in the min is also in maj. if not, it's not + // a subset at all. + integer i; + for (i = 0; i < llGetListLength(min_con); i++) { + string curr = llList2String(min_con, i); + integer where = find_in_list(maj_con, curr); + if (where < 0) return FALSE; + } + // every item was present. yippee. + return TRUE; +} + +// returns an assessment of the two sets in terms of the +// possible relationships between them. +string consider_mu(string set_a, string set_b) +{ + // firstly, validate these set names. + integer where_a = find_set(set_a); + if (where_a < 0) return POSSIBILITY_ERRONEOUS; // not known. + integer where_b = find_set(set_b); + if (where_b < 0) return POSSIBILITY_ERRONEOUS; + if (set_a == set_b) return POSSIBILITY_ONE_MEANING; // easy check. + // check whether they have any common members at all. + list inter = set_intersection(set_a, set_b); + if (inter == []) return POSSIBILITY_MUTUAL_EXCLUDE; + // see if one set is contained in the other. + if (proper_subset(set_a, set_b) + || proper_subset(set_b, set_a) ) { + return POSSIBILITY_THREE_POSSES; + } + // resign ourselves to loading up the sets to check equality. + integer len_a = llGetListLength(get_set_by_index(where_a)); + integer len_b = llGetListLength(get_set_by_index(where_b)); + if (len_a == len_b) { + // this is the only way they can be equivalent. + // simple interpretation of this currently... + list diff = set_difference(set_a, set_b); + if (diff == []) return POSSIBILITY_ONE_MEANING; + // so now, although they are of equal length, we know they are not + // equal sets. + } + // if our logic is correct, there is only one option left. + return POSSIBILITY_FOUR_POSSES; +} + +// this should be invoked from the link_message event handler to process the requests +// for whatever service this library script provides. +handle_link_message(integer sender, integer huff_id, string msg, key id) +{ +//log_it("link msg: " + (string)sender + " " + (string)huff_id + " msg=" + msg + " id=" + (string)id); + // separate the list out + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + + // we interpret the "msg" as a command. + if (msg == DEFINE_SET_CMD) { + string name = llList2String(parms, 0); + parms = llDeleteSubList(parms, 0, 0); + integer to_return = define_set(name, parms); + send_reply(sender, [ to_return ], msg); + } else if (msg == REMOVE_SET_CMD) { + string name = llList2String(parms, 0); + integer to_return = remove_set_by_name(name); + send_reply(sender, [ to_return ], msg); + } else if (msg == ADD_ELEMENTS_CMD) { + string name = llList2String(parms, 0); + parms = llDeleteSubList(parms, 0, 0); + integer to_return = add_items(name, parms); + send_reply(sender, [ to_return ], msg); + } else if (msg == CUT_ELEMENTS_CMD) { + string name = llList2String(parms, 0); + parms = llDeleteSubList(parms, 0, 0); + integer to_return = remove_items(name, parms); + send_reply(sender, [ to_return ], msg); + } else if (msg == INTERSECT_CMD) { + string name1 = llList2String(parms, 0); + string name2 = llList2String(parms, 1); + list to_return = set_intersection(name1, name2); + send_reply(sender, to_return, msg); + } else if (msg == UNION_CMD) { + string name1 = llList2String(parms, 0); + string name2 = llList2String(parms, 1); + list to_return = set_union(name1, name2); + send_reply(sender, to_return, msg); + } else if (msg == DIFFERENCE_CMD) { + string name1 = llList2String(parms, 0); + string name2 = llList2String(parms, 1); + list to_return = set_difference(name1, name2); + send_reply(sender, to_return, msg); + } else if (msg == WHAT_MU_CMD) { + string name1 = llList2String(parms, 0); + string name2 = llList2String(parms, 1); + string to_return = consider_mu(name1, name2); + send_reply(sender, [ to_return ], msg); + } else if (msg == GET_SET_CMD) { + string name = llList2String(parms, 0); + send_reply(sender, get_set_by_name(name), msg); + } else if (msg == LIST_SET_NAMES_CMD) { + send_reply(sender, set_names, msg); + } else if (msg == CLEAR_ALL_CMD) { + set_names = []; + all_sets = []; + send_reply(sender, [ 1 ], msg); + } else { + log_it("unknown set command: msg=" + msg + " id=" + (string)id); + } +} + + +////////////// +// +// from hufflets... + +// diagnostic hufflets... + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on an unusual channel for chat if it's not intended for general public. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat that anyone can hear. we take off the bling for this one. +// llSay(0, to_say); +} + +////////////// + +// string processing hufflets... + +// the string processing methods are not case sensitive. + +// returns the index of the first occurrence of "pattern" inside +// the "full_string". if it is not found, then a negative number is returned. +integer find_substring(string full_string, string pattern) +{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); } + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return find_substring(compare_with, prefix) == 0; } + +////////////// + +// sim-related hufflets... + +// returns TRUE if the value in "to_check" specifies a legal x or y value in a sim. +integer valid_sim_value(float to_check) +{ + if (to_check < 0.0) return FALSE; + if (to_check >= 257.0) return FALSE; + return TRUE; +} + +// returns TRUE if the "to_check" vector is a location outside of the current sim. +integer outside_of_sim(vector to_check) +{ + return !valid_sim_value(to_check.x) || !valid_sim_value(to_check.y); +} + +////////////// + +// list processing hufflets... + +// locates the string "text" in the list to "search_in". +integer find_in_list(list search_in, string text) +{ + integer len = llGetListLength(search_in); + integer i; + for (i = 0; i < len; i++) { + if (llList2String(search_in, i) == text) + return i; + } + return -1; +} + +// removes the entry at "index" and instead inserts the list "to_insert" +// at that position. +list replace_entry(list to_modify, integer index, list to_insert) +{ + if (llGetListLength(to_modify) == 0) return to_insert; // special case for empty. + return llListReplaceList(to_modify, to_insert, index, index); +} + +// returns the portion of the list between start and end, but only if they are +// valid compared with the list length. an attempt to use negative start or +// end values also returns a blank list. +list chop_list(list to_chop, integer start, integer end) +{ + integer last_len = llGetListLength(to_chop) - 1; + if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return []; + return llList2List(to_chop, start, end); +} + +////////////// + +// joins a list of sub-items using the item sentinel for the library. +string wrap_item_list(list to_wrap) +{ return llDumpList2String(to_wrap, HUFFWARE_ITEM_SEPARATOR); } + +// handles when blank strings need to come through the pipe. +string wrap_blank_string(string to_wrap) +{ + if (llStringLength(to_wrap)) return to_wrap; // that one is okay. + return "\"\""; // return a quoted nothing as a signal for a blank. +} + +// undoes a previously wrapped blank string. +string interpret_blank_string(string to_unwrap) +{ + if (to_unwrap == "\"\"") return ""; // that was an encoded blank. + return to_unwrap; // no encoding. +} + +// a simple version of a reply for a command that has been executed. the parameters +// might contain an outcome or result of the operation that was requested. +send_reply(integer destination, list parms, string command) +{ +//log_it("reply set: " + dump_list(parms)); + llMessageLinked(destination, SET_COMPARATOR_HUFFWARE_ID + REPLY_DISTANCE, command, + llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR)); +} + +// returns a printable form of the list. +string dump_list(list to_show) +{ + integer len = llGetListLength(to_show); + integer i; + string text; + for (i = 0; i < len; i++) { + string next_line = llList2String(to_show, i); + if (find_substring(next_line, " ") >= 0) { + // this guy has a space in it, so quote it. + next_line = "'" + next_line + "'"; + } + text += next_line; + if (i < len - 1) text += " "; + } + return text; +} + +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { +// log_it("memory left " + (string)llGetFreeMemory()); +//hmmm: turn this into a report function. + } + + link_message(integer sender, integer num, string str, key id) { + if (num != SET_COMPARATOR_HUFFWARE_ID) return; + handle_link_message(sender, num, str, id); + } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/slate_reader_v1.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/slate_reader_v1.2.lsl new file mode 100755 index 00000000..66059a4b --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/slate_reader_v1.2.lsl @@ -0,0 +1,363 @@ + +// huffware script: slate reader, by fred huffhines +// +// uses the noteworthy library to read a set of notecards. the list of notecards +// can be queried, and the current notecard's contents can be read aloud or sent +// to a link as link messages. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// global constants... + +string SLATE_SIGNATURE = "#slate"; + // the notecard must begin with this as its first line for it to be + // recognized as our configuration card. + +// global variables... + +integer response_code; // set to uniquely identify the notecard read in progress. +integer global_link_num; // which prim number to deliver the notecard contents to. + +// implements an API for moving between the active notecards. +////////////// +// do not redefine these constants. +integer SLATE_READER_HUFFWARE_ID = 10028; + // the unique id within the huffware system for the noteworthy script to + // accept commands on. this is used in llMessageLinked as the num parameter. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +////////////// +string RESET_SLATE_READER_COMMAND = "#rsslt"; + // causes the notecard information to be forgotten and the script restarted. +string SR_GET_INFORMATION_COMMAND = "#infy"; + // used by clients to ask for information about the current number of notecards + // available, and their names. this information is sent back on the huffware ID + // plus the reply distance. first parm is the number, and the rest are the names. +string SR_PLAY_CARD_COMMAND = "#playvo"; + // picks a particular notecard for reading and send the notecard's contents in a + // series of link messages, using this command and the reply distance. there are + // two parameters: an integer for the notecard number to read (from 0 through the + // number of notecards - 1) and the link number to send the messages to. +////////////// + +// requires noteworthy library v10.4 or better. +////////////// +// do not redefine these constants. +integer NOTEWORTHY_HUFFWARE_ID = 10010; + // the unique id within the huffware system for the noteworthy script to + // accept commands on. this is used in llMessageLinked as the num parameter. +////////////// +string BAD_NOTECARD_INDICATOR = "bad_notecard"; + // indicates that the notecard reading process has failed to find an appropriate one. +string NOTECARD_READ_CONTINUATION = "continue!"; + // returned as first parameter if there is still more data to handle. +// commands available via the noteworthy library: +string READ_NOTECARD_COMMAND = "#read_note#"; + // command used to tell the script to read notecards. needs a signature to find + // in the card as the first parameter, and a randomly generated response code for + // the second parameter. the response code is used to uniquely identify a set of + // pending notecard readings (hopefully). the signature can be blank. + // the results will be fired back as the string value returned, which will have + // as first element the notecard's name (or BAD_NOTECARD_INDICATOR if none was + // found) and as subsequent elements an embedded list that was read from the + // notecard. this necessarily limits the size of the notecards that we can read + // and return. +string READ_SPECIFIC_NOTECARD_COMMAND = "#read_thisun#"; + // like the read notecard command, but specifies the notecard name to use. only that + // specific notecard file will be consulted. first and second parm are still signature + // and response code, third parm is the notecard name. +// +////////////// +// joins a list of parameters using the parameter sentinel for the library. +string wrap_parameters(list to_flatten) +{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); } +////////////// + +// this function fires off a request to the noteworthy library via a link message. +// noteworthy will look for a notecard with our particular signature in it and +// if it finds one, it will read the configuration therein. an empty string is +// returned if noteworthy couldn't find anything. +request_configuration(string note_name) +{ + // try to find a notecard with our configuration. + response_code = -1 * (integer)randomize_within_range(23, 80000, FALSE); + string parms_sent = wrap_parameters([SLATE_SIGNATURE, response_code, note_name]); + llMessageLinked(LINK_THIS, NOTEWORTHY_HUFFWARE_ID, READ_SPECIFIC_NOTECARD_COMMAND, + parms_sent); +} + +// provides the functions of the slate reader API. +process_slate_requests(string msg, key id) +{ + if (msg == RESET_SLATE_READER_COMMAND) { + llResetScript(); + } else if (msg == SR_GET_INFORMATION_COMMAND) { + list parms = [ llGetInventoryNumber(INVENTORY_NOTECARD) ]; + integer indy; + for (indy = 0; indy < llGetInventoryNumber(INVENTORY_NOTECARD); indy++) { + parms += llGetInventoryName(INVENTORY_NOTECARD, indy); + } + llMessageLinked(LINK_THIS, SLATE_READER_HUFFWARE_ID + REPLY_DISTANCE, msg, + wrap_parameters(parms)); + } else if (msg == SR_PLAY_CARD_COMMAND) { + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + integer note_number = llList2Integer(parms, 0); + integer link_number = llList2Integer(parms, 1); + initialize_reader(note_number, link_number); + } +} + +// processes link messages received from the noteworthy library. +handle_link_message(integer which, integer num, string msg, key id) +{ + if (num == SLATE_READER_HUFFWARE_ID) { + process_slate_requests(msg, id); + return; + } + if ( (num != NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE) + || (msg != READ_NOTECARD_COMMAND) ) + return; // not for us. + // process the result of reading the notecard. + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + string notecard_name = llList2String(parms, 0); + integer response_for = llList2Integer(parms, 1); + if (response_for != response_code) return; // oops, this isn't for us. + if (notecard_name == BAD_NOTECARD_INDICATOR) { + // we hated the notecards we found, or there were none. + log_it("We apologize; there seem to be no notecards with a first line of '" + + SLATE_SIGNATURE + + "'. We can't read any configuration until that situation improves."); + } else { + // snag all but the first two elements for our config now. + list config_list = llList2List(parms, 2, -1); + // a valid notecard has been found. + integer lines_to_say = llGetListLength(config_list); + integer indy; + for (indy = 0; indy < lines_to_say; indy++) { + string line = llList2String(config_list, indy); + if (!is_prefix(line, "#")) { + llMessageLinked(global_link_num, SLATE_READER_HUFFWARE_ID + REPLY_DISTANCE, + SR_PLAY_CARD_COMMAND, line); + } + } + } +} + +/////////////// + +initialize_reader(integer note_number, integer link_number) +{ + // reset our relevant variables. + global_link_num = link_number; + + if (note_number >= llGetInventoryNumber(INVENTORY_NOTECARD)) { + llSay(0, "Cannot initialize reader to notecard number " + (string)note_number + " because that is out of range."); + return; + } + string notecard_name = llGetInventoryName(INVENTORY_NOTECARD, note_number); + + // request that the noteworthy library start looking for our notecard. + request_configuration(notecard_name); +} + +////////////// +// from hufflets... +// + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on an unusual channel for chat if it's not intended for general public. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat that anyone can hear. we take off the bling for this one. +// llSay(0, to_say); +} + +// locates the string "text" in the list to "search_in". +integer find_in_list(list search_in, string text) +{ + integer len = llGetListLength(search_in); + integer i; + for (i = 0; i < len; i++) { + if (llList2String(search_in, i) == text) + return i; + } + return -1; +} + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return (llSubStringIndex(compare_with, prefix) == 0); } + +// returns a number at most "maximum" and at least "minimum". +// if "allow_negative" is TRUE, then the return may be positive or negative. +float randomize_within_range(float minimum, float maximum, integer allow_negative) +{ + if (minimum > maximum) { + // flip the two if they are reversed. + float temp = minimum; minimum = maximum; maximum = temp; + } + float to_return = minimum + llFrand(maximum - minimum); + if (allow_negative) { + if (llFrand(1.0) < 0.5) to_return *= -1.0; + } + return to_return; +} + +// strips the spaces off of the beginning and end of a string. +string strip_spaces(string to_strip) +{ + // clean out initial spaces. + while (llGetSubString(to_strip, 0, 0) == " ") + to_strip = llDeleteSubString(to_strip, 0, 0); + // clean out ending spaces. + while (llGetSubString(to_strip, -1, -1) == " ") + to_strip = llDeleteSubString(to_strip, -1, -1); + return to_strip; +} + +// locates the item with "name_to_find" in the inventory items with the "type". +// a value from 0 to N-1 is returned if it's found, where N is the number of +// items in the inventory. +integer find_in_inventory(string name_to_find, integer inv_type) +{ + integer num_inv = llGetInventoryNumber(inv_type); + if (num_inv == 0) return -1; // nothing there! + integer inv; + for (inv = 0; inv < num_inv; inv++) { + if (llGetInventoryName(inv_type, inv) == name_to_find) + return inv; + } + return -2; // failed to find it. +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.8. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +// end hufflets +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { + auto_retire(); + } + +// on_rez(integer parm) { llResetScript(); } + + // process the response from the noteworthy library. + link_message(integer which, integer num, string msg, key id) + { handle_link_message(which, num, msg, id); } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/star_chair_sitting_config_v0.4.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/star_chair_sitting_config_v0.4.lsl new file mode 100755 index 00000000..8016e42f --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/star_chair_sitting_config_v0.4.lsl @@ -0,0 +1,11 @@ +#sitting +# for star chair. +avatar_rotation = <0, 0, -90> +avatar_position = <-0.1, -0.28, -0.1> +which_floor_is_this=1 +base_floor_height = 0 +floor_size_in_meters=0 +unseat_afterwards = 0 +pause_before_eviction = 0.28 +camera_eye_offset = <3, 2, 1.5> +camera_target = <-3, 0, 1> diff --git a/huffware/huffotronic_updater_freebie_v5.3/take_touches_v0.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/take_touches_v0.2.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/take_touches_v0.2.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/take_touches_v0.2.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/text_label_v3.9.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/text_label_v3.9.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/text_label_v3.9.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/text_label_v3.9.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/texture_mover_v3.0.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/texture_mover_v3.0.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/texture_mover_v3.0.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/texture_mover_v3.0.lsl diff --git a/huffware/huffotronic_updater_freebie_v5.3/texture_shower_v2.7.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/texture_shower_v2.7.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/texture_shower_v2.7.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/texture_shower_v2.7.lsl diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/viewscreen_blitter_v3.9.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/viewscreen_blitter_v3.9.lsl new file mode 100755 index 00000000..beed1fc7 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/viewscreen_blitter_v3.9.lsl @@ -0,0 +1,205 @@ + +// huffware script: viewscreen blitter, by fred huffhines. +// +// listens for web addresses to show and puts them on the screen of this prim. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// important switches... + +integer IS_OPENSIM = TRUE; + // selects the major mode of operation for the viewscreen. + +integer DEBUGGING = TRUE; + // if this is set to true, more diagnostic info is printed than normal. + +// global constants... + +// API for the viewscreen blitter library... +////////////// +integer VIEWSCREEN_BLITTER_HUFFWARE_ID = 10027; + // unique ID for the viewscreen services. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +// +string SHOW_URL_COMMAND = "#shurl"; + // requests the viewscreen to show a particular URL as its texture. +string RESET_VIEWSCREEN_COMMAND = "#shrz"; + // resets the viewscreen script to the default state. +string SHOW_TEXTURE_COMMAND = "#shtex"; + // displays a texture on the prim. the first parameter is the texture key or name. +////////////// + +// configurable constants... + +handle_view_request(string command, string parms) +{ + if (command == SHOW_URL_COMMAND) { + // the parms is just the URL to display. + if (DEBUGGING) { + string to_show = "url is: "; + integer i; + for (i = 0; i < llStringLength(parms); i += 512) { + // show the string a chunk at a time or we will be censored by chat limits. + integer end = i + 511; + if (i >= llStringLength(parms) - 1) i = llStringLength(parms) - 1; + to_show += llGetSubString(parms, i, end); + llSay(0, to_show); + to_show = ""; + } + } + if (!IS_OPENSIM) { + // second life implementation... + llParcelMediaCommandList( [ PARCEL_MEDIA_COMMAND_STOP, + PARCEL_MEDIA_COMMAND_TYPE, "text/html", + PARCEL_MEDIA_COMMAND_URL, parms ] ); + // we separate out the looping player call since we can't rely on the new media + // setting being ready within the same media call. + llParcelMediaCommandList( [ PARCEL_MEDIA_COMMAND_PLAY, PARCEL_MEDIA_COMMAND_LOOP ] ); + } else { + // opensim implementation... + string extra_parms = ""; + //bgcolor:white,alpha:false,width:1024,height:768, + string url_texture_key = osSetDynamicTextureURL("", "image", parms, extra_parms, 5000); +//string url_texture_key =""; + +//llSay(0, "dyn texture is " + url_texture_key); + if (url_texture_key != "") { + llSetTexture(url_texture_key, ALL_SIDES); + } + } + } else if (command == RESET_VIEWSCREEN_COMMAND) { + llResetScript(); + } else if (command == SHOW_TEXTURE_COMMAND) { + llSetLinkTexture(LINK_THIS, parms, ALL_SIDES); + } +} + +////////////// +// from hufflets... + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +// end hufflets. +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + llParcelMediaCommandList( [ PARCEL_MEDIA_COMMAND_STOP ] ); + } + + link_message(integer linknum, integer num, string cmd, key parms) { + if (num != VIEWSCREEN_BLITTER_HUFFWARE_ID) return; // not for us. + handle_view_request(cmd, parms); + } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/weapon_fire_v2.2.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/weapon_fire_v2.2.lsl new file mode 100755 index 00000000..5e3908ea --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/weapon_fire_v2.2.lsl @@ -0,0 +1,219 @@ + +// huffware script: weapon fire, by fred huffhines. +// +// finds the first item in inventory and uses it as a bullet. it also finds the first +// sound in inventory and uses that as the report noise for firing the bullet. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// modifiable constants... + +integer LEFT_HANDED = TRUE; + +//float SPEED = 100.0; +float SPEED = 20.0; + +float DELAY = 0.0; // an enforced delay before the weapon can fire again. + +float VOLUME = 0.2; + +integer USE_ANIMATION = FALSE; // if this is true, then the bearer is posed. + +// global variables... + +vector velocity; +vector position; +rotation rotation_vec; + +integer have_permissions = FALSE; +integer armed = TRUE; + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +// performs the act of firing the bullet. +fire() +{ + if (armed) { + rotation_vec = llGetRot(); + velocity = llRot2Fwd(rotation_vec); + position = llGetPos(); + position = position + velocity; + position.z += 0.75; + velocity = velocity * SPEED; + if (llGetInventoryNumber(INVENTORY_SOUND) > 0) + llTriggerSound(llGetInventoryName(INVENTORY_SOUND, 0), VOLUME); + integer i; + for (i = 0; i < llGetInventoryNumber(INVENTORY_OBJECT); i++) + llRezObject(llGetInventoryName(INVENTORY_OBJECT, i), position, velocity, rotation_vec, 2814); + if (DELAY != 0.0) { + armed = FALSE; + llSetTimerEvent(DELAY); + } + } +} + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + if (llGetInventoryNumber(INVENTORY_SOUND) > 0) + llPreloadSound(llGetInventoryName(INVENTORY_SOUND, 0)); + if (!have_permissions) { + llRequestPermissions(llGetOwner(), + PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS); + } + } + + on_rez(integer param) { llResetScript(); } + + run_time_permissions(integer permissions) { + if (permissions == PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS) { + llTakeControls(CONTROL_ML_LBUTTON, TRUE, FALSE); + if (USE_ANIMATION) { + if (LEFT_HANDED) { + llStartAnimation("hold_L_bow"); + llStopAnimation("aim_L_bow"); + } else { + llStartAnimation("hold_R_handgun"); + llStopAnimation("aim_R_handgun"); + } + } + have_permissions = TRUE; + } + } + + attach(key attachedAgent) { + if (attachedAgent != NULL_KEY) { + llRequestPermissions(llGetOwner(), + PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS); + } else { + if (have_permissions) { + llStopAnimation("hold_R_handgun"); + llStopAnimation("aim_R_handgun"); + llReleaseControls(); + llSetRot(<0,0,0,1>); + have_permissions = FALSE; + } + } + } + + control(key name, integer levels, integer edges) { + if ( ((edges & CONTROL_ML_LBUTTON) == CONTROL_ML_LBUTTON) + && ((levels & CONTROL_ML_LBUTTON) == CONTROL_ML_LBUTTON) ) { + fire(); + } + } + + timer() { + llSetTimerEvent(0.0); + armed = TRUE; + } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/welcomebot_v4.0.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/welcomebot_v4.0.lsl new file mode 100755 index 00000000..e12750c2 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/welcomebot_v4.0.lsl @@ -0,0 +1,529 @@ + +// huffware script: welcomebot visitor list, by fred huffhines +// +// originally based on LSL wiki examples. +// how to use this script: +// 1) create an object that you want to track visitors to your area. +// 2) change the parameters below to reflect your own information. +// 3) add the script to your tracker object. +// 4) reply to the script's rez email with the following commands: +// list: send the current visitor list. +// scan: scan the area near the greeter object. +// clear: throw out the visitor list. sends a copy before deleting. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +//todo: +// how do we reconcile having a config notecard with the giveaway ones? +// just name it special? +// we want the script not to reset when the parameters change! +// it should just reset the notecard stuff and other variables. +// no new setup on email and all that stuff! +// maintain list of last visit times. +// just as a string? + + +// put your e-mail address here. +string owner_email = "fred@gruntose.com"; + +// put the GUIDs of your business partners here. the owner is automatically +// notified. +list notification_list = []; + +// place name used to identify this visitor list. +string greeter_name = "eepaw shop welcomebot"; + +// the description of where this is. +string welcome_message = "Welcome to the eepaw shop (Eclectic Electric Patterns and Widgets). Please touch the welcomebot for more information."; + +// you can make the bot listen on a different channel if you like... +integer OWNER_CHANNEL = 14; + +integer MAX_ITEMS_PER_LINE = 4; // how many visitors to show on one line of output. + +integer EMAIL_RECEPTION_ENABLED = FALSE; // currently not enabled in opensim. + +integer EMAIL_SENDING_ENABLED = FALSE; // apparently many sims don't allow email sending. + +integer MAX_VISITORS_TRACKED = 120; + // the number of items we keep around in the list of visitors. + +float pause_when_mail_pending = 2.0; // check for mail this frequently when there's a stack. +float pause_no_pending_mail = 42.0; // if no mail had been waiting, we check more slackly. + +// number of seconds between sending the owner a visitor list. +integer auto_list_interval = 43200; // 43200 is every 12 hours. + +// any variables that need to be modified are above this... +/////////////////////////////////////////////////////////// + +float SENSOR_RANGE = 10.0; // in meters. + +float SENSOR_RATE = 30.0; // in seconds. + +// Global variables +string regionName; +key myKey; +list visitor_list; +list current_list; +string scan_list; +float maxZ = 0.0; +float minZ = 0; +integer MAXZ = 20; +integer MINZ = 20; +integer last_automatic_list_sent; // when we last updated the owner automatically. + +// sends an email. somewhat a thin wrapper. +send_email(string recipient, string subject, string text) +{ + if (EMAIL_SENDING_ENABLED) { + // some sims do not support email. + llEmail(recipient, subject, text); + } +} + +cloaked_owner_alert(string text) +{ + // temporarily change the name so emails will come from the expected greeter. + string hold_name = llGetObjectName(); + llSetObjectName(greeter_name); + // send the message. + llOwnerSay(text); + // restore our name back to the original. + llSetObjectName(hold_name); +} + +cloaked_IM(key who_to_tell, string text) +{ + // temporarily change the name so emails will come from the expected greeter. + string hold_name = llGetObjectName(); + llSetObjectName(greeter_name); + // send the message. + llInstantMessage(who_to_tell, text); + // restore our name back to the original. + llSetObjectName(hold_name); +} + +// notifies all the people in the list who want to hear about visitors. +notify_folks(string text) +{ + // temporarily change the name so emails will come from the expected greeter. + string hold_name = llGetObjectName(); + llSetObjectName(greeter_name); + + list temp_notify = notification_list; + temp_notify += [llGetOwner()]; + + integer i; + for (i = 0; i < llGetListLength(temp_notify); i++) { + string current_id = llList2String(temp_notify, i); + if ((key)current_id == llGetOwner()) cloaked_owner_alert(text); + else cloaked_IM((key)current_id, text); + } + + // restore our name back to the original. + llSetObjectName(hold_name); +} + +// looks for the name provided in a list. the index is returned if +// the name is found, otherwise a negative number is returned. +integer find_name_in_list(string name, list avlist) +{ + integer len = llGetListLength(avlist); + integer i; + for (i = 0; i < len; i++) + { + if (llList2String(avlist, i) == name) + return i; + } + return -1; +} + +// looks for changes in the current locale. +check_region(list detected) +{ + // we accumulate information about people who have changed state in the variables below. + string names_entering = ""; + string names_leaving = ""; + integer num_entering = 0; + integer num_leaving = 0; + + // see if we have somebody new. + integer len = llGetListLength(detected); + integer i; + for (i = 0; i < len; i++) { + string name = llList2String(detected, i); + if (find_name_in_list(name, current_list) < 0) { + current_list += name; + if (num_entering > 0) names_entering += ", "; + names_entering += name; + num_entering++; + } + } + + // see if somebody left. + len = llGetListLength(current_list); + for (i = 0; i < len; i++) { + string name = llList2String(current_list, i); + if (find_name_in_list(name, detected) < 0) { + if (num_leaving > 0) names_leaving += ", "; + names_leaving += name; + num_leaving++; + } + } + + // update current list to be current only + current_list = detected; + + // create a message describing the state. + string msg = ""; + if (num_entering > 0) msg = "Arrived: " + names_entering; + if (num_leaving > 0) { + if (llStringLength(msg) > 0) msg += "\n"; + msg += "Left: " + names_leaving; + } + if (msg != "") notify_folks(msg); +} + +string build_visitor_list() +{ + string text = greeter_name + ":\n"; + integer len = llGetListLength(visitor_list); + integer i; + integer items_per_line = 0; + for (i = len - 1; i >= 0; i--) { + if (items_per_line != 0) { + text += "\t"; + } + text += llList2String(visitor_list, i); + items_per_line++; + if (items_per_line >= MAX_ITEMS_PER_LINE) { + text += "\n"; + items_per_line = 0; + } + } + // handle case for last line. + if (items_per_line != 0) text += "\n"; + text += "Total=" + (string)len; + return text; +} + +send_list_in_text() +{ + llOwnerSay(build_visitor_list()); +} + +send_scan_in_text() +{ + llOwnerSay(greeter_name + " scan\n" + scan_list); +} + +send_list_in_email() +{ + string text = build_visitor_list(); + send_email(owner_email, greeter_name + " visitor list", text); +} + +send_scan_in_email() +{ + send_email(owner_email, greeter_name + " scan", scan_list); +} + +clear_list() +{ + visitor_list = []; +} + +initialize_greeter() +{ + last_automatic_list_sent = llGetUnixTime(); + + scan_list = ""; + + myKey = llGetKey(); + regionName = llGetRegionName(); + + vector rezPos = llGetPos(); + maxZ = rezPos.z + MAXZ; + minZ = rezPos.z - MINZ; + string rezInfo = "Key: "+(string)myKey + " @ "+regionName+" "+(string)rezPos+" ("+(string)maxZ+")"; + notify_folks(rezInfo); + send_email(owner_email, greeter_name + " Rez Information", rezInfo); + llSetTimerEvent(pause_no_pending_mail); + llSensorRepeat( "", "", AGENT, SENSOR_RANGE, TWO_PI, SENSOR_RANGE); + // hook up the bot to the radio so it will hear commands. + llListen(OWNER_CHANNEL, "", llGetOwner(), ""); + llOwnerSay("Listening to commands on channel " + (string)OWNER_CHANNEL + + " (list, scan, clear)"); +} + +// performs the requested command. +process_command_locally(string msg) +{ + if (msg == "list") send_list_in_text(); + else if (msg == "scan") send_scan_in_text(); + else if (msg == "clear") { + send_list_in_text(); + clear_list(); + } +} + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { +//log_it("invpo=" + (string)posn); + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. +//log_it("pos=" + (string)space_v_posn); + } + if (space_v_posn < 2) return []; // no space found. +//log_it("space v@" + (string)space_v_posn); + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { +//log_it("indy=" + (string)space_v_posn); + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); +//log_it("saw case of previously redundant item, aieee. flattened: " + to_chop_up); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default +{ + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { + auto_retire(); + initialize_greeter(); + } + + on_rez(integer param) { llResetScript(); } + + timer() + { + if (EMAIL_RECEPTION_ENABLED) { + llGetNextEmail("", ""); // check for email with any subject/sender + + integer curTime = llGetUnixTime(); + if (curTime - last_automatic_list_sent < auto_list_interval) return; + last_automatic_list_sent = curTime; + send_list_in_email(); + } + } + + email(string time, string address, string subj, string message, integer num_left) + { + list args = llParseString2List(message, [" "], []); + if (llGetListLength(args) < 1) return; // nothing to do. + string cmd = llToLower(llList2String(args, 0)); +//room for a process_command_for_email here. + if (cmd == "list") send_list_in_email(); + else if (cmd == "scan") send_scan_in_email(); + else if (cmd == "clear") { + send_list_in_email(); + clear_list(); + } + + if (num_left == 0) llSetTimerEvent(pause_no_pending_mail); // sleep longer before checking. + else llSetTimerEvent(pause_when_mail_pending); // short sleep since others are waiting. + } + + listen(integer channel, string name, key id, string msg) + { + if ( (channel != OWNER_CHANNEL) || (id != llGetOwner()) ) return; // not for them. + process_command_locally(msg); + } + + // when a visitor touches the welcomebot, hand out the first landmark and + // first notecard we have stored. + touch_start(integer count) { + string item_name = llGetInventoryName(INVENTORY_NOTECARD, 0); // first notecard. + if (item_name != "") + llGiveInventory(llDetectedKey(0), item_name); + item_name = llGetInventoryName(INVENTORY_LANDMARK, 0); + if (item_name != "") + llGiveInventory(llDetectedKey(0), item_name); + } + + sensor(integer number_detected) + { + list detected = []; + vector pos = llGetPos(); + string textVerbose = ""; + string textNewVerbose = ""; + string textNew = ""; + integer other = 0; + integer group = 0; + integer new = 0; + integer outer; + + textVerbose = ""; + for (outer = 0; outer < number_detected; outer++) + { + vector dpos = llDetectedPos(outer); + if (llGetLandOwnerAt(dpos)==llGetLandOwnerAt(llGetPos())) + { + string detected_name = llDetectedName(outer); + string detected_key = llDetectedKey(outer); + integer info_res = llGetAgentInfo(detected_key); + float diff = llVecDist(pos,dpos); + integer dist = llRound(diff); + string result = detected_name+" ("+detected_key+") " + (string)dist; + if(dpos.y>pos.y) result+="N"; + else if(dpos.ypos.x) result+="E"; + else if(dpos.x= 0 && dpos.x <= 256) && + (dpos.y >= 0 && dpos.y <= 256) && + (dpos.z >= minZ) && (dpos.z <= maxZ)) + { + if (llDetectedGroup(outer)) group++; + else other++; + + // add to the detected list + detected += detected_name; + + // see if they are on the visitor list + if (find_name_in_list(detected_name, visitor_list) < 0) { + // make sure we haven't gone beyond our limit. + if (llGetListLength(visitor_list) >= MAX_VISITORS_TRACKED) { + visitor_list = llDeleteSubList(visitor_list, 0, 0); + } + // add to the visitor list + visitor_list += detected_name; + + // handle any separators to make more readable + if (new > 0) { + textNew += ", "; + textNewVerbose += "\n"; + } + + // add to the text + textNew += detected_name; + textNewVerbose += detected_name+" ("+detected_key+") @ "+regionName+" "+(string)dpos; + result += "N"; + + // increment the count of new visitors + new++; + cloaked_IM(detected_key, welcome_message); + } + } + else + { + result += "*"; + } + textVerbose += result + "\n"; + } + } + + if (new > 0) { + // extra verbose logging. +// notify_folks(textNew + "\n" + textNewVerbose); + } + scan_list = textVerbose; + + check_region(detected); + } + + no_sensor() + { + check_region([]); + } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/wylie_controller_v8.4.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/wylie_controller_v8.4.lsl new file mode 100755 index 00000000..873bbf27 --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/wylie_controller_v8.4.lsl @@ -0,0 +1,624 @@ + +// huffware script: wylie controller, by fred huffhines and wam7c macchi. +// +// accepts wylie input and distributes the command to the various parts of the letter. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// configurable values... the settings below are just defaults. + +integer LISTENING_CHANNEL = 0; // the channel where we'll listen for commands. +integer ONLY_LISTEN_TO_OWNER = FALSE; // if true, only the owner's voice counts. +integer SHOW_CHANNEL = TRUE; // puts a title over our heads if true. +vector TITLE_COLOR = <1.0, 1.0, 1.0>; // the color the title should be. + +integer DEBUGGING = FALSE; // if this is true, then the debugging will be noisy. + +// constants... do not change these. + +integer STARTUP_CODE_NOTECARD_RETRIES = 14042; + // secret code used to launch the wylie writers with many notecard retries. + +// channels for speech / link messages. +integer LOWER_VOWEL_CHANNEL = 11008; +integer CONSONTANT_CHANNEL = 21008; +integer UPPER_VOWEL_CHANNEL = 31008; + +string RESET_TEXTURE_WORD = "reset-texture"; + // tells the letter blocks to go back to default texture. +string BROADCAST_WORD = "bcast"; + // used to tell many wylie writers to do something at the same time. + +string WYLIE_NOTEWORTHY_SIGNATURE = "#wylie"; + // the notecard must begin with this as its first line for it to be + // recognized as our configuration card. + +integer SNOOZE_BEFORE_CONFIG_RESET = 14; + // number of seconds to wait before trying to read notecard again. + +// requires noteworthy library v10.2 or better. +////////////// +// do not redefine these constants. +integer NOTEWORTHY_HUFFWARE_ID = 10010; + // the unique id within the huffware system for the noteworthy script to + // accept commands on. this is used in llMessageLinked as the num parameter. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +string NOTECARD_READ_CONTINUATION = "continue!"; + // returned as first parameter if there is still more data to handle. +string BAD_NOTECARD_INDICATOR = "bad_notecard"; + // indicates that the notecard reading process has failed to find an appropriate one. +string BUSY_READING_INDICATOR = "busy_already"; + // this return value indicates that the script is already in use by some other script. + // the calling script should try again later. +// commands available via the noteworthy library: +string READ_NOTECARD_COMMAND = "#read_note#"; + // command used to tell the script to read notecards. needs a signature to find + // in the card as the first parameter, and a randomly generated response code for + // the second parameter. the response code is used to uniquely identify a set of + // pending notecard readings (hopefully). the signature can be empty or missing. + // the results will be fired back as the string value returned, which will have + // as first element the notecard's name (or "bad_notecard" if none was found) and + // as subsequent elements an embedded list that was read from the notecard. this + // necessarily limits the size of the notecards that we can read and return. +// +////////////// + +// global variables that track notecard reading and any configuration found. +string global_notecard_name; // name of our notecard in the object's inventory. +integer response_code; // set to uniquely identify the notecard read in progress. +list global_config_list; // a collection of configuration parameters from our notecard. +integer global_config_index; // allows wrap-around feature, which we don't use here. + +integer startup_parm = 0; // the startup parameter for the script. +integer retries_allowed = 1; // number of attempts to read notecards. + +// sets the object up. +initialize_wylie() +{ + // listen for commands from the appropriate person(s) on the right channel. + key id = NULL_KEY; + if (ONLY_LISTEN_TO_OWNER) id = llGetOwner(); + llListen(LISTENING_CHANNEL, "", id, ""); + // display a text title if we were configured to. + float title_alpha = 1.0; + if (!SHOW_CHANNEL || !LISTENING_CHANNEL) title_alpha = 0.0; + llSetText("/" + (string)LISTENING_CHANNEL, TITLE_COLOR, title_alpha); + if (LISTENING_CHANNEL) { + // if we're not on open chat listen, then we still peek out on the + // open chat channel for certain instructions. + llListen(0, "", id, ""); + } + llSetTimerEvent(SNOOZE_BEFORE_CONFIG_RESET); // snooze a bit, and try reading again. + + if (startup_parm == STARTUP_CODE_NOTECARD_RETRIES) { +///hmmm: parameterize + retries_allowed = 11; + startup_parm = -3; // no longer allowed to hit unless we get restarted. + } + retries_allowed--; +} + +// tells all the character blocks to go back to their default texture. +reset_textures() +{ + set_texture(LOWER_VOWEL_CHANNEL, RESET_TEXTURE_WORD); + set_texture(CONSONTANT_CHANNEL, RESET_TEXTURE_WORD); + set_texture(UPPER_VOWEL_CHANNEL, RESET_TEXTURE_WORD); +} + +// makes one of the blocks show the tibetan character, if it knows it. +set_texture(integer block_number, string tib_char_wylie) +{ + llMessageLinked(LINK_SET, block_number, tib_char_wylie, NULL_KEY); +} + +// find the vowel in the string provided and separate the word into its +// components for each of the blocks (in the order: upper, middle, lower). +list consume_wylie(string maybe_wylie_in) +{ + string maybe_wylie = llToLower(maybe_wylie_in); // drop to lower case. + // if it has a space in it, then it's not for us. + integer posn = llSubStringIndex(maybe_wylie, " "); + if (posn >= 0) return []; // wrong answer. + list vowels = [ "a", "e", "i", "o", "u" ]; + integer vow_indy; + for (vow_indy = 0; vow_indy < llGetListLength(vowels); vow_indy++) { + string curr_vow = llList2String(vowels, vow_indy); + posn = find_substring(maybe_wylie, curr_vow); + if (posn >= 0) { + // found one! +// log_it("found the vowel " + curr_vow); + +//hmmm: here is where better logic would be needed to support multi-vowel words. + +//hmmm: here is also where better logic needed for processing words, with multiple chars. +// that would just require splitting the remainder off instead of having the rule about +// vowel must be at end. + + // compute the consonant portion. + string consonant = llGetSubString(maybe_wylie, 0, posn - 1) + "a"; + // we guess that the consonant is about right already. + // dratted special cases for specific characters. + if (maybe_wylie == curr_vow + "h") consonant = "ah"; + else if (maybe_wylie == curr_vow) consonant = "a"; +// log_it("consonant now is " + consonant); + + // now calculate the right vowel(s) and where they live. + string lower_vowel; + string upper_vowel; + if (curr_vow == "a") { + // no extra vowel markers, since there's an 'a'. + lower_vowel = RESET_TEXTURE_WORD; + upper_vowel = RESET_TEXTURE_WORD; + } else if (curr_vow == "u") { + // our one vowel that goes below. + lower_vowel = curr_vow; + upper_vowel = RESET_TEXTURE_WORD; + } else { + // mainstream vowels fly high. + lower_vowel = RESET_TEXTURE_WORD; + upper_vowel = curr_vow; + } +// log_it("success, answering: " + upper_vowel + " / " + consonant + " / " + lower_vowel); + return [ upper_vowel, consonant, lower_vowel ]; + } + + } + // no vowel was found!? + return []; +} + +////////////// + +// joins a list of parameters using the parameter sentinel for the library. +string wrap_parameters(list to_flatten) +{ return llDumpList2String(to_flatten, HUFFWARE_PARM_SEPARATOR); } + +////////////// + +// this function fires off a request to the noteworthy library via a link message. +// noteworthy will look for a notecard with our particular signature in it and +// if it finds one, it will read the configuration therein. an empty string is +// returned if noteworthy couldn't find anything. +request_configuration() +{ + global_notecard_name = ""; // reset any previous card. + global_config_list = []; // toss previous contents. + // try to find a notecard with our configuration. + response_code = -1 * (integer)randomize_within_range(23, 80000, FALSE); + string parms_sent = wrap_parameters([WYLIE_NOTEWORTHY_SIGNATURE, response_code]); + llMessageLinked(LINK_THIS, NOTEWORTHY_HUFFWARE_ID, READ_NOTECARD_COMMAND, + parms_sent); +} + +// processes link messages received from the noteworthy library. +integer handle_link_message(integer which, integer num, string msg, key id) +{ +//log_it("got linkmsg: " + (string)which + " - " + (string)num + " - " + msg); + if (num != NOTEWORTHY_HUFFWARE_ID + REPLY_DISTANCE) return FALSE; // not for us. + // process the result of reading the notecard. + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + string notecard_name = llList2String(parms, 0); + integer response_for = llList2Integer(parms, 1); + if (response_for != response_code) return FALSE; // oops, this isn't for us. + if (notecard_name == BAD_NOTECARD_INDICATOR) { +// should we try again? + return FALSE; // drop that one. + } + + // a valid notecard has been found. +//log_it("notecard name: " + notecard_name); + // snag all but the first two elements for our config now. + global_config_list += llList2List(parms, 2, -1); + if (notecard_name == BUSY_READING_INDICATOR) { + llSetTimerEvent(SNOOZE_BEFORE_CONFIG_RESET); + return FALSE; + } else if (notecard_name != NOTECARD_READ_CONTINUATION) { + global_notecard_name = notecard_name; // record its name for later use. + // now we're done reading, process the file as a set of definitions. + global_config_index = 0; // we are starting over in the config list. + process_ini_config(); + return TRUE; + } else { + // we're going to keep reading; notecard has more. + llSetTimerEvent(SNOOZE_BEFORE_CONFIG_RESET); + return FALSE; + } +} + +/////////////// + +// consumes the notecard in a very application specific way to retrieve our configuration items. +// the example script only looks for two variables: name and description. if those are found in +// the sample card, then they are proudly shown. +parse_variable_definition(string to_parse) +{ +//log_it("parse_var: " + to_parse); + string content; // filled after finding a variable name. + if ( (content = get_variable_value(to_parse, "channel")) != "") { + LISTENING_CHANNEL = (integer)content; + } else if ( (content = get_variable_value(to_parse, "owner_only")) != "") { + ONLY_LISTEN_TO_OWNER = (integer)content; + } else if ( (content = get_variable_value(to_parse, "show_channel")) != "") { + SHOW_CHANNEL = (integer)content; + } else if ( (content = get_variable_value(to_parse, "title_color")) != "") { + TITLE_COLOR = (vector)content; + } +} + +initialize_notecard_reader() +{ + // reset our relevant variables. + global_notecard_name = ""; + global_config_list = []; + global_config_index = 0; + // reset our configurable constants to the defaults. + LISTENING_CHANNEL = 0; + ONLY_LISTEN_TO_OWNER = FALSE; + SHOW_CHANNEL = TRUE; + TITLE_COLOR = <1.0, 1.0, 1.0>; + + // request that the noteworthy library start looking for our notecard. + request_configuration(); + + llSetTimerEvent(SNOOZE_BEFORE_CONFIG_RESET); // snooze a bit, and try reading again. +} + +hear_the_voices(integer channel, string name, key id, string message) +{ + if (ONLY_LISTEN_TO_OWNER && (id != llGetOwner())) return; // not valid from this guy. + if (!channel && (id == llGetOwner())) { +///&& LISTENING_CHANNEL && + // this is general chat, which is not our listening channel. we only + // allow special owner commands here. + if (is_prefix(message, BROADCAST_WORD)) { + // whoa, they know the secret broadcasting code. + channel = LISTENING_CHANNEL; + // chop the bcast word, plus a space, from the command. + message = llDeleteSubString(message, 0, llStringLength(BROADCAST_WORD)); + } + } + if (channel != LISTENING_CHANNEL) return; // this is not for us. + if (message == "reset") { + reset_textures(); + return; + } else if ( (message == "self-destruct") && (id == llGetOwner())) { + // this is going to send us to object heaven, we hope. + llDie(); + } + // wasn't a command to reset, so let's see if we know it. + list chopped = consume_wylie(message); + if (llGetListLength(chopped) != 3) return; // not understood as even close to wylie. + set_texture(UPPER_VOWEL_CHANNEL, llList2String(chopped, 0)); + set_texture(CONSONTANT_CHANNEL, llList2String(chopped, 1)); + set_texture(LOWER_VOWEL_CHANNEL, llList2String(chopped, 2)); +} + +////////////// + +// from hufflets... + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on an unusual channel for chat if it's not intended for general public. +// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat that anyone can hear. we take off the bling for this one. +// llSay(0, to_say); +} + +// the string processing methods are not case sensitive. + +// returns TRUE if the "pattern" is found in the "full_string". +integer matches_substring(string full_string, string pattern) +{ return (find_substring(full_string, pattern) >= 0); } + +// returns the index of the first occurrence of "pattern" inside +// the "full_string". if it is not found, then a negative number is returned. +integer find_substring(string full_string, string pattern) +{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); } + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return find_substring(compare_with, prefix) == 0; } + +// strips the spaces off of the beginning and end of a string. +string strip_spaces(string to_strip) +{ + // clean out initial spaces. + while (llGetSubString(to_strip, 0, 0) == " ") + to_strip = llDeleteSubString(to_strip, 0, 0); + // clean out ending spaces. + while (llGetSubString(to_strip, -1, -1) == " ") + to_strip = llDeleteSubString(to_strip, -1, -1); + return to_strip; +} + +// parses a variable definition to find the name of the variable and its value. +// this returns two strings [X, Y], if "to_split" is in the form X=Y. +list separate_variable_definition(string to_split) +{ + integer equals_indy = llSubStringIndex(to_split, "="); + // we don't support missing an equals sign, and we don't support it as the first character. + if (equals_indy <= 0) return []; // no match. + string x = llGetSubString(to_split, 0, equals_indy - 1); + string y = llGetSubString(to_split, equals_indy + 1, -1); + to_split = ""; // save space. + return [ strip_spaces(x), strip_spaces(y) ]; +} + +// returns a non-empty string if "to_check" defines a value for "variable_name". +// this must be in the form "X=Y", where X is the variable_name and Y is the value. +string get_variable_value(string to_check, string variable_name) +{ + list x_y = separate_variable_definition(to_check); + if (llGetListLength(x_y) != 2) return ""; // failure to parse a variable def at all. + if (!is_prefix(llList2String(x_y, 0), variable_name)) return ""; // no match. + return llList2String(x_y, 1); // a match! +} + +// returns a number at most "maximum" and at least "minimum". +// if "allow_negative" is TRUE, then the return may be positive or negative. +float randomize_within_range(float minimum, float maximum, integer allow_negative) +{ + if (minimum > maximum) { + // flip the two if they are reversed. + float temp = minimum; minimum = maximum; maximum = temp; + } + float to_return = minimum + llFrand(maximum - minimum); + if (allow_negative) { + if (llFrand(1.0) < 0.5) to_return *= -1.0; + } + return to_return; +} + +// examines all entries that we got from the notecard to see if any contain definitions. +// this is basically an INI file reader, but it uses a list instead of a file. +// ini files provide a format with multiple sections of config information, like so: +// [section_1] +// name1=value1 +// name2=value2 ...etc... +// [section_2] +// name1=value1 ...etc... +process_ini_config() +{ + integer indy; + integer count = llGetListLength(global_config_list); + + // iterate across the items in our configuration to look for ones that are not done yet. + for (indy = global_config_index; indy < count; indy++) { + string line = llList2String(global_config_list, indy); + // search for a section beginning. + if (llGetSubString(line, 0, 0) == "[") { + // we found the start of a section name. now read the contents. + indy++; // skip section line. + } + integer sec_indy; + for (sec_indy = indy; sec_indy < count; sec_indy++) { + // read the lines in the section. + line = llList2String(global_config_list, sec_indy); + if (llGetSubString(line, 0, 0) != "[") { + // try to interpret this line as a variable setting. this is just + // one example of a way to handle the config file; one might instead + // want to do something below once a whole section is read. + parse_variable_definition(line); + indy = sec_indy; // track that we've passed this line. + } else { + // we're at the beginning of a new section now, so start processing its + // configuration in the outer loop. + indy = sec_indy - 1; // set indy to proper beginning of section. + global_config_index = indy; // remember where we had read to. + sec_indy = count + 3; // skip remainder of inner loop. + } + } + } + + global_config_index = 0; // reset outer position if want to re-read. +} + +// locates the item with "name_to_find" in the inventory items with the "type". +// a value from 0 to N-1 is returned if it's found, where N is the number of +// items in the inventory. +integer find_in_inventory(string name_to_find, integer inv_type) +{ + integer num_inv = llGetInventoryNumber(inv_type); + if (num_inv == 0) return -1; // nothing there! + integer inv; + for (inv = 0; inv < num_inv; inv++) { + if (llGetInventoryName(inv_type, inv) == name_to_find) + return inv; + } + return -2; // failed to find it. +} + +////////////// + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +//defect in default state: +// if no response from noteworthy library, infinite hang. + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { startup_parm = parm; state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() + { + if (DEBUGGING) log_it("started, free mem=" + (string)llGetFreeMemory()); + auto_retire(); // make sure newest addition is only version of script. + if (DEBUGGING) log_it("entered default state."); + initialize_wylie(); + initialize_notecard_reader(); + } + + state_exit() { llSetTimerEvent(0); } + + // process the response from the noteworthy library. + link_message(integer which, integer num, string msg, key id) + { + if (handle_link_message(which, num, msg, id)) + state ready_for_instructions; + } + + // restart when we see changes to our notecard configuration. + changed(integer change) { + if (change & CHANGED_INVENTORY) { + llSleep(4); // delay to avoid rez-time premature startup. + if (DEBUGGING) log_it("restarting due to inv change."); + state rerun; + } + } + + // invoked when the notecard reading has failed. + timer() { + if (global_notecard_name == "") { + if (retries_allowed < 0) { + if (DEBUGGING) log_it("ran out of retries, skipping reset."); + state ready_for_instructions; + } + if (DEBUGGING) log_it("timed out, going back to default."); + state default; // start over again. + } + } + + listen(integer channel, string name, key id, string message) { + hear_the_voices(channel, name, id, message); + } + + on_rez(integer parm) { startup_parm = parm; state rerun; } +} + +state ready_for_instructions +{ + state_entry() { + if (DEBUGGING) log_it("entered ready_for_instructions state."); + initialize_wylie(); + } + + state_exit() { llSetTimerEvent(0); } + + listen(integer channel, string name, key id, string message) { + hear_the_voices(channel, name, id, message); + } + + // reset when we see changes to our notecard configuration. + changed(integer change) { + if (change & CHANGED_INVENTORY) { + if (DEBUGGING) log_it("inventory change seen; restarting"); + state rerun; + } + } + + on_rez(integer parm) { startup_parm = parm; state default; } +} diff --git a/huffware/huffotronic_eepaw_knowledge_v60.9/zap_updater_from_elevators_v1.3.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/zap_updater_from_elevators_v1.3.lsl new file mode 100755 index 00000000..6dee724d --- /dev/null +++ b/huffware/huffotronic_eepaw_knowledge_v60.9/zap_updater_from_elevators_v1.3.lsl @@ -0,0 +1,187 @@ + +// huffware script: zap updater from elevators, by fred huffhines +// +// this script is an evil little assassin that is supposed to remove the updater +// script from the objects it's added to. it should not remove that script from +// any huffotronic devices though. +// this is an attempt to quell some SL stability problems due to some seeming +// combination of numbers of objects and numbers of scripts. +// we hope to be able to add the updater back in automatically later because the +// script pin should still be set. let's see if osgrid has that behavior the same +// as second life did. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + + +// from hufflets... + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.8. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +// returns the index of the first occurrence of "pattern" inside +// the "full_string". if it is not found, then a negative number is returned. +integer find_substring(string full_string, string pattern) +{ return llSubStringIndex(llToLower(full_string), llToLower(pattern)); } + +// returns TRUE if the "prefix" string is the first part of "compare_with". +integer is_prefix(string compare_with, string prefix) +{ return find_substring(compare_with, prefix) == 0; } + +// returns true if this object is a huffotronic updater of some sort. +integer inside_of_updater() +{ + return find_substring(llGetObjectName(), "huffotronic") >= 0; +} + +// stops all the scripts besides this one. +knock_around_other_scripts(integer running_state) +{ + integer indy; + integer insider = inside_of_updater(); + string self_script = llGetScriptName(); + // we set all other scripts to the running state requested. + for (indy = 0; indy < llGetInventoryNumber(INVENTORY_SCRIPT); indy++) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, indy); + if ( (curr_script != self_script) + && (!insider || matches_server_script(curr_script)) ) { + // this one seems ripe for being set to the state requested. + llSetScriptState(curr_script, running_state); + } + } +} + +string SERVER_SCRIPT = "huffotronic update server"; + // the prefix of our server script that hands out updates. + +// returns true if a script is a version of our update server. +integer matches_server_script(string to_check) +{ + return is_prefix(to_check, SERVER_SCRIPT); +} + +// end hufflets. +////////////// + +default +{ + state_entry() + { + auto_retire(); + if (inside_of_updater()) return; // do nothing else. + + llWhisper(0, "hello, i'm jeeves. pleased to meet you."); + llSleep(32); + + string gotta_zap = "huff-update client v"; + integer i; + for (i = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; i >= 0; i--) { + string cur = llGetInventoryName(INVENTORY_SCRIPT, i); + if (is_prefix(cur, gotta_zap)) { + llWhisper(0, "i, jeeves, will now clean out this script: " + cur); + llRemoveInventory(cur); + } + } + llSleep(14); + knock_around_other_scripts(TRUE); + llSleep(4); + llWhisper(0, "i'm sorry to say sir that i now must bid you adieu, as i am removing myself from the world."); + llSleep(1); + llRemoveInventory(llGetScriptName()); + } +} + diff --git a/huffware/huffotronic_updater_freebie_v5.3/zenmondos_mailbox_v0.4.lsl b/huffware/huffotronic_eepaw_knowledge_v60.9/zenmondos_mailbox_v0.4.lsl similarity index 100% rename from huffware/huffotronic_updater_freebie_v5.3/zenmondos_mailbox_v0.4.lsl rename to huffware/huffotronic_eepaw_knowledge_v60.9/zenmondos_mailbox_v0.4.lsl -- 2.34.1