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

LSL Wiki : PetProtocol021

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

version 0.21
*note: the "obey" function in the .pet script is unfinished and needs to be commented out to compile.
*todo: the .pet script needs to keep an array of the last few (10?) pets it has come in contact with, probably indexed by their gPet_key. The gPet_key is the last section of their UUID with the letters stripped out. The array will be used by the "obey" function to determine what the commanding pet's "food chain" number is.


pet protocol (note)

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. (a + sign indicates it's a required attributes, otherwise it's optional)
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
string gCurrentAction;
list gActions; // Action probabilities
list gObey; // obey values for threat, neutral, prey, owner, other avatar
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_key;
key gOwner;

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_key = (integer)llDumpList2String(llParseString2List(llGetSubString(llGetKey(), 27, 37),
        ["-","a","b","c","d","e","f"], []), ""); 
        // the numbers in the last 10 digits of this pet's key.
//llWhisper(0, (string)gPet_key + " "+ (string)llGetKey());
gL4 = llListen (gPet_key, "", "", "");
gTime = 0;
llSetTimerEvent(10); //move this to a better place
read_behavior();
}

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",
    10,"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
}


// 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.

obey ( string command, string this_pet, string commander )
{
    float this_pet_flee = 
    float this_pet_fight = 
    float commander_flee = 
    float commander_fight = 
    
    // test if commander is a threat
    if ( llList2Float(commander,fight) > this_pet.flee )
    {
        if (llList2Float(obey,1) > llFrand(1)) // calculate chance of obeying the command
            llMessageLinked(command);
    }
    // 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);
    }
}

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

//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);
if (llGetSubString(message, 0, 6) == "command")
    obey(message, gBehavior_0, gBehavior_1); // respond to commands
// (respond to requests)
// (respond to questions)
// (make note of introductions)
    }
    
    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)
            {
                llDeleteSubString(str, wordBegin, wordBegin+6);
                llInsertString(str, wordBegin, (string)llGetOwner());
                llWhisper(0, "modified command is " + str);
            }

            wordBegin = llSubStringIndex(str, "|mykey");
            if (wordBegin != -1)
            {
                llDeleteSubString(str, wordBegin, wordBegin+6);
                llInsertString(str, wordBegin, (string)llGetKey());
                llWhisper(0, "modified command is " + str);
            }
            
        }
    }
}
There is no comment on this page. [Display comments/form]