Don't click here unless you want to be banned.

LSL Wiki : PetProtocol032

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are crawl338.us.archive.org
Back to PetProtocol main page

version 0.32
* If you make 2 pets they will attempt to give each other commands. For debugging purposes, the pets will say what command they hear.


(note)
pet protocol

The goal is to develop a protocol that would allow pets to communicate and interact with other pets and avatars.

1) easy to understand and modify
2) open source which anyone can copy for free from the forum.
- Please do not post your ideas to this thread if you don't want other people to use it. =]
- It is completely acceptable if you wish to use any or all of the code in this post in your pets and sell them for profit.

We need some scale to show where the animal fits into the food chain and show what other animals would be concidered a threat, prey, or playmate.

food chain 0.0 - 1.0
*This scale is off the top of my head and Very likely to be changed.*
0.0 grass
0.1 sheep
0.15 birds, mice
0.2 house cats
0.3 dogs
0.7 hippo
0.8 shark
0.9 lion
1.0 godzilla

A pet will chat it's status on a the introduction channel (9000) once every 10 seconds.

(If 2 pets decide they want to chase each other they agree to communicate more often on some other channel.. need to think about that more.)

Here are the attributes to be chatted every 10 seconds.
attribute example
fight 0.3 what would the pet approach
flee 0.4 what would the pet run from
type dog
subtype beagle
action chase, hide, beg, eat
target sylvester/grass/bp (use target's key instead of name?)
offsetR relative offset from target. <-5,0,0> would follow 5m behind target
offsetA absolute offset from target. <-5,0,0> would follow 5m west of target
- - - -
inferred info (can be detected when the pet chats)
name spot (pet's name = object name)
key a0deec52-f289-7608-7064-a8bcf480e6d8
distance distance in meters, often a radius

now to list some behaviors or "action" (a + sign indicates a target is required - such as another pet or avatar)
+ eat
+ chase
+ avoid
+ follow/swarm/flock
+ bump/push
+ ride/perch
+ mimic
play_dead
speak
sit
stay
sleep
+ jump_over
dig
+ circle
+ play_tag
+ beg
find
give/deliver
retrieve/fetch
meet
face/look

example introduction string:
"introduction + fight|0.3 + flee|0.4 + type|dog + subtype|beagle + action|watching + target|owner + offsetR| <-5,0,0> + group|6500"

example command string:
"command + action|follow + target|mykey + offsetR|<-5,0,0>"

[ question + knowncommands|1 ]

[ answer + knowcommands|1 + follow| + sit| ]



.pet (script)

string gBehavior_0; // this pet's behavior
string gBehavior_1; // one other pet's behavior
list glBehaviors_MRU = [0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0  ,0,0,0]; // last 10 pet keys and their fight/flee parameters "ID, fight, flee"
// string gCurrentAction; delete?
list gActions; // Action probabilities
list gObey; // obey values for threat, neutral, prey, owner, other avatar
list glCurrentMessage; // the most recently received message
float gTime = 0; //sum of all action probabilities
integer gL1; // listen handler for owner
integer gL2; // listen handler for pet introduction channel
integer gL3; // listen handler for group channel
integer gL4; // listen handler for pet's private channel
integer gIntroduction_channel = 9000;
integer gGroup_channel = 1; //this value should be defined in the read_behavior function;
integer gPet_channel;
key gOwner;

// get_channel grabs the numbers in the last 9 digits of this pet's key. We will use this integer as this pet's private chat channel.
integer get_pet_channel(key ID_key)
{
    integer temp_ID = (integer)llDumpList2String(llParseString2List(llGetSubString(ID_key, 28, 37), ["-","a","b","c","d","e","f"], []), "");
    return temp_ID;
}

initialize()
{
gOwner = llGetOwner();
llListenRemove(gL1);
llListenRemove(gL2);
llListenRemove(gL3);
//integer llListen(integer channel, string name, key id, string msg);
gL1 = llListen (0, "", gOwner,"");
gL2 = llListen (gIntroduction_channel, "", "", "");
gL3 = llListen (gGroup_channel, "", "", "");
gPet_channel = get_pet_channel(llGetKey());

// llWhisper(0, (string)gPet_key + " "+ (string)llGetKey());
gL4 = llListen (gPet_channel, "", "", "");
gTime = 0;
llSetTimerEvent(10); //move this to a better place
read_behavior();
}

//string get_param_value (string)
//{
    
    
read_behavior()
{
// read behavior from notecard, email, or XML-RPC
read_notecard();

gBehavior_0 = "fight|0.3 + flee|0.4 + type|dog + subtype|beagle + action|watching + target|master + group|6500";
llWhisper(0,(string)gBehavior_0);

gActions = [
    40,"sleep",
    1,"speak",
    20,"follow + target|owner",
    10,"evade + target|owner + offsetR| <-0,0,0> + distance|4.5",
    30,"command + action|eat + target|owner",
    10,"command + action|stay",
    50,"command + action|follow + target|mykey + offsetR|<-5,0,0>"
    ];

// the following sums the probabilities of the possible actions
    integer i;
    integer max = llGetListLength(gActions);
    for (i=0;i<max;i+=2)
    {
        gTime = gTime + llList2Float(gActions, i);
//        llWhisper(0,(string)gTime);
    }

gObey = [1.0, 0.5, 0.1, 0.9, 0.5];

}

read_notecard()
{
// Read description
// Read actions list
}

write_behavior() {// IM's or emails owner the current behavior
}


//////////////////////////////////////////////////////////////////////////
// CHOOSE BEHAVIOR
// mod: mood modifier:
//      < 1 will only do the first mod% of the behaviors
//      = 1 will do behaviors exactly as specified (will do a behavior every time)
//      > 1 has increasing chance of doing nothing
// time: the sum of all 'chances' much faster to calculate this ahead of time then as you go
// behaviors: A list that looks like [chance, "behavior_command", chance, "behavior_command"
//        ex: [0.4, "bark", 0.2, "sit"] or [4, "bark", 2, "sit"]
// Num and target are sent with the link message.  Perhaps always call this with the key of
// any currently targeted animal?  Will work fine if 0 and "" are used respectively.
//
// Example call, although behavior should be a list stored in a variable made from reading a notecard...
// choose_behavior(1,0.6,[0.4,"sleep",0.2,"bark"],0,"");
choose_behavior(float mod, float time, list behaviors, integer num, key target)
{
    float choice = llFrand(time * mod);
//    llWhisper(0,"time = "+(string)time);
//    llWhisper(0,"choice = "+(string)choice);
    integer i;
    integer max = llGetListLength(behaviors);
    for (i=0;i<max;i+=2)
    {
        if (choice < (float)llList2String(behaviors,i))
        {
            //Do this action!
            llMessageLinked(LINK_SET,num,llList2String(behaviors,i+1),target);
//            llWhisper(0,llList2String(behaviors,i+1));
llSetText(llList2String(behaviors,i+1), <0.5,0.5,1>,1);
            return;
        }
        else
        {
            choice -= llList2Float(behaviors,i);
        }
    }
}


introduce()
{
    llSay(gIntroduction_channel, "introduction + "+gBehavior_0);    
}

//////////////////////////////////////////////////////////////////////////
// OBEY function
// Any incoming commands are sent to this function. This script determines
// if the command will be obeyed based on values from the pet's behavior
// setup. If the command will be obeyed, it will be sent out to the other
// scripts in the pet.
// There are 5 parameters for the obey variable, 
// (threat, neutral, prey, owner, other avatar),
// each ranging from 0 to 1. 
// If the parameter is 0, the pet will never obey a command from something
// in that class, if it's 1 the pet will always obey that command.
integer obey ( string command, string string_this_pet, key commander )
{
//gBehavior_0 = "fight|0.3 + flee|0.4 + type|dog + subtype|beagle + action|watching + target|master + group|6500";
    list list_this_pet = llParseString2List(string_this_pet, ["|", "+", " "], []);
llSay(0, "list_this_pet is "+llDumpList2String(list_this_pet, ","));
//integer llSubStringIndex(string source, string pattern)
    float this_pet_flee = llList2Float(list_this_pet, (llListFindList(list_this_pet, ["flee"])+1));
    float this_pet_fight = llList2Float(list_this_pet, (llListFindList(list_this_pet, ["fight"])+1));
//check that we actually have a flee & fight value for commander in our MRU. if not, assume commander has fight of 0, flee of 1.
    float commander_flee = 1;
    float commander_fight = 0;
//NEED COMMANDER'S KEY
//THEN SEARCH THE MRU FOR THE KEY
integer commander_in_MRU = llListFindList(glBehaviors_MRU, [(string)commander]);
    if ( commander_in_MRU != -1 )
    {
llSay(0, "commander_in_MRU is "+(string)commander_in_MRU);
//IF THE COMMANDER'S KEY IS IN OUR MRU
//READ THE FIGHT AND FLEE FROM OUR MRU
        commander_flee = llList2Float(glBehaviors_MRU, (commander_in_MRU+1));
llSay(0, "llListFindList(list_this_pet) is "+(string)(llListFindList(list_this_pet, ["flee"])+1));

        commander_fight = llList2Float(glBehaviors_MRU, commander_in_MRU+2 );
//***to do*** make sure numbers are between 0.0 and 1.0
    }
    integer obey = 0;

llSay(0, "glBehaviors_MRU is "+llDumpList2String(glBehaviors_MRU, ","));
llSay(0, "this_pet_flee is "+(string)this_pet_flee+" & this_pet_fight is "+(string)this_pet_fight);
llSay(0, "commander_flee is "+(string)commander_flee+" & commander_fight is "+(string)commander_fight);
    
// test if commander is a threat
    if ( commander_fight > this_pet_flee )
    {
llSay(0, (string)commander_fight +">"+ (string)this_pet_flee);
        if (llList2Float(gObey,0) > llFrand(1)) // calculate chance of obeying the command
        {
            llMessageLinked(-4, 0, command, NULL_KEY);
            obey = TRUE;
        }
    }
    // neutral
//    if ( commander.fight > this_pet.flee )
    {
//        if (llList2Float(obey,2) > llFrand(1)) // calculate chance of obeying the command
//            llMessageLinked(command);
    }
    // prey
//    else if ( this_pet.fight > commander.flee )
    {
//        if (llList2Float(commander,3) > llFrand(1)) // calculate chance of obeying the command
//            llMessageLinked(command);
    }
    // owner
//    else if ( other == gOwner )
    {
//        if (llList2Float(commander,4) > llFrand(1)) // calculate chance of obeying the command
//            llMessageLinked(command);
    }
    //other
//    else if ( other != gOwner )
    {
//        if (llList2Float(commander,5) > llFrand(1)) // calculate chance of obeying the command
//            llMessageLinked(command);
    }
    return obey;
}

default
{
    state_entry()
    {
        initialize();
    }
    
    on_rez(integer start_param)
    {
        initialize();
    }

   listen(integer channel, string name, key id, string message)
   {
//  if id = gOwner
//  if id = pet_key
//  if id = pet_group
//    list lPet_key = [(string)get_pet_channel(id)];
//***to do***do a bounds check before converting to list?
    list glCurrentMessage = llParseString2List(message, ["|", "+", " "], []);
//gBehavior_1 = 
llSetText(llKey2Name(id)+": "+(string)message, <1,0,0>,1);
llSleep(0.01);
llSetText(llKey2Name(id)+": "+(string)message, <1,1,1>,1);
//llWhisper(0,(string)message);
//llSay(0, llList2String(glCurrentMessage, 0));
if (llList2String(glCurrentMessage, 0) == "command")
    obey(message, gBehavior_0, id); // decide if this pet should obey the command
// (respond to requests)
// (respond to questions)
// (make note of introductions)
if (llList2String(glCurrentMessage, 0) == "introduction")
{
// glBehaviors_MRU = [ "a",  "0",  "0",  "0",  "0",  "0",  "0",  "b",  "9916", "h" ];
    integer Pet_key_in_MRU = llListFindList(glBehaviors_MRU, [(string)id]); // find where this pet is in our contact list
//llWhisper(0,"lPet_key "+(string)lPet_key+" is at '"+(string)Pet_key_in_MRU+"'");

//==================
//llSay(0, "glBehaviors_MRU is "+llDumpList2String(glBehaviors_MRU, ","));
//==================


// 1) See if the pet is already in the list
// 2) If they are in the list, remove them from the list and add them to the front of the list
// 3) if they are not in the list, add them to the front of the list and delete whatever is at the end of the list
    if (Pet_key_in_MRU == -1)  // if the pet is not in our contact list, add it and it's fight/flee parameters to the front of the list and delete the last pet on the list
    {
            // find the fight/flee parameters
            list temp_list = llParseString2List(message, ["|"," + "], []);
float fight_value = llList2Float(temp_list, (llListFindList(temp_list, ["fight"])+1));
float flee_value =  llList2Float(temp_list, (llListFindList(temp_list, ["flee"])+1));
//        llInstantMessage(llGetOwner(), "fight_value = '"+(string)fight_value+"'");
//        llInstantMessage(llGetOwner(), "flee_value = '"+(string)flee_value+"'");
glBehaviors_MRU = [(string)id, fight_value, flee_value]+glBehaviors_MRU;

glBehaviors_MRU = llDeleteSubList(glBehaviors_MRU, 27, 29);

//glBehaviors_MRU = ["A", "5", "orange"]+glBehaviors_MRU;
//    llInstantMessage(llGetOwner(), "glBehaviors_MRU = '"+(string)glBehaviors_MRU+"'");
//        llInstantMessage(llGetOwner(), "lPet_key = '"+(string)llget_param_value("fight|")+"'");
    }
    else 
    {
            // find the fight/flee parameters
    list temp_list = llParseString2List(message, ["|"," + "], []);
    float fight_value = llList2Float(temp_list, (llListFindList(temp_list, ["fight"])+1));
    float flee_value =  llList2Float(temp_list, (llListFindList(temp_list, ["flee"])+1));
glBehaviors_MRU = llDeleteSubList(glBehaviors_MRU, Pet_key_in_MRU, Pet_key_in_MRU+2);
glBehaviors_MRU = [(string)id, fight_value, flee_value]+glBehaviors_MRU;
     
    }
    
    }

    }
    
    changed(integer change)
    {
// read_notecard;
    }
    
    timer()
    {
// if not in the middle of behavior??
introduce();
//choose_behavior(float mod, float time, list behaviors, integer num, key target)
choose_behavior(1,gTime,gActions,0,"");

// stay_close_to_owner();
    }

}


beagle behavior (note)
May want to use a delimiter other than "," for vectors and quats. Maybe "/" or "*" ?


// default description
// [fight (0-1), flea (0-1), type, subtype, default action, target, rotation, group channel]
// example: 0.3, 0.4, "Dog", "Beagle", "sitting", "", <0,0,0>, 6500;

description = "fight|0.3 + flee|0.4+ type|dog + subtype|beagle + action|watching + target|owner + group|6500"

// next are the actions this pet will do and their percentage change.
// they don't need to add up to 1. they are relative to their total.

sleep, 0.3;
chase, 0.2;

// next are the actions this pet will do if specific pet types are detected.

cat, chase, 1.0;
mouse, evade, 1.0;

// next is the percentage chance that this pet will obey commands
// the 1st number is chance to obey a command from a pet with a higher fight number than this pet, ie. a pet concidered a threat.
// the 2nd number is chance to obey a command from a pet that is neither a threat nor prey
// the 3rd number is chance to obey a command from a pet that is prey
// the 4th number is chance to obey a command from its owner
// the 5th number is chance to obey a person other than the owner
// obey, threat, neutral, prey, owner, other_avatar
obey, 1.0, 0.5, 0.1, 0.9, 0.5;

follow (script)

// follow script
// 

startup()
{
    llSensorRemove();
    llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Y, TRUE);
    llSetStatus(STATUS_ROTATE_Z, TRUE);
    llSetStatus(STATUS_PHANTOM, TRUE);
    llSensorRepeat( "", llGetOwner(), AGENT, 100, PI, 0.5 );
}

default
{
    state_entry()
    {
        startup();
    }    
    on_rez(integer param)
    {
        startup();
    }
    
    link_message(integer sender_num, integer num, string str, key id)
    {
        if (str == "follow")
            llWhisper(0, "follow");
            
                llSensorRepeat( "", id, AGENT, 100, PI, 0.5 );

    }

    no_sensor()
    {
        llSetTimerEvent(30); // the ride may be lost, die in 30 seconds
    }
    timer(){
        llWhisper(0, "cannot find target ");
        //llDie();
    } // haven't detected the user for 30 seconds, so delete the ride
    sensor( integer total_number )
    {
        vector vTarget = llDetectedPos(0);
        rotation rTarget = llDetectedRot(0);
        vector vOffset = <-2.0,0,-0.2>; // offset from the agent's center

        vector vDestination = vTarget + vOffset * rTarget;
//        llLookAt(vDestination, 2, 0.2);
        
        llMoveToTarget(vDestination, .1);

        llLookAt(vTarget + <0,0,vOffset.z>, 1, 0.1);

//        llMoveToTarget(vTarget, 0.5);
        llSetTimerEvent(0.0); // turns off the die timer
    }
    touch_start(integer total_number)
    {
        llSay(0, "stopit.");
    }
}


speak (script)

default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if (str == "speak")
            llWhisper(0, "bark");
    }
}


COMMAND (script)

// this script is untested

// COMMAND script (output)
// If chosen action is to command other pets to do an action
// this script replaces constants with values, and sends it out.

default
{
    link_message(integer sender_num, integer num, string str, key id)
    {
        if (llGetSubString(str, 0, 6) == "command")
        {
//            llWhisper(0, "outgoing command is " + str);
            integer wordBegin;
            wordBegin = llSubStringIndex(str, "|owner");
            if (wordBegin != -1)
            {
                str = llDeleteSubString(str, wordBegin+1, wordBegin+5);
                str = llInsertString(str, wordBegin+1, (string)llGetOwner());
//                llWhisper(0, "modified command is " + str);
            }

            wordBegin = llSubStringIndex(str, "|mykey");
            if (wordBegin != -1)
            {
                str = llDeleteSubString(str, wordBegin+1, wordBegin+5);
                str = llInsertString(str, wordBegin+1, (string)llGetKey());
//                llWhisper(0, "modified command is " + str);
            }
            llWhisper(9000, str); // whisper the command on channel 9000.
            //***to do*** this channel needs to be picked more intelligently            
        }
    }
}
There are 3 comments on this page. [Display comments/form]