+++ /dev/null
-
-// 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 + <t_x, t_y, 0.0>);
- 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 + <t_x, t_y, 0.0>);
- 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 <x, y, z> 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 += <llFrand(2) * ATTACK_X_MARGIN - ATTACK_X_MARGIN,
- llFrand(2) * ATTACK_Y_MARGIN - ATTACK_Y_MARGIN,
- llFrand(2) * ATTACK_Z_MARGIN - ATTACK_Z_MARGIN>;
- } else {
-//log_it("normal target calc");
- TARGET_POSITION += < 0.0, 0.0, DEFAULT_HEIGHT_ABOVE_FOLLOWED_OBJECT>;
- TARGET_POSITION += <llFrand(2) * DEFAULT_X_MARGIN - DEFAULT_X_MARGIN,
- llFrand(2) * DEFAULT_Y_MARGIN - DEFAULT_Y_MARGIN,
- llFrand(2) * DEFAULT_Z_MARGIN - DEFAULT_Z_MARGIN>;
- }
- // 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 = <curr_pos.x, curr_pos.y, curr_pos.z + 1>;
- 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.
-