Lets Make a Breedable - Part 6

Posted on

In this part we are going to extend our brain script to support interaction with the owner of the pet to do things like freeze, set home, range, name and so on. These changes will be done in the running state.

First we will need some additional global variables.

integer dialog_channel;
integer dialog_listener;
integer range_channel;
integer range_listener;
integer name_channel;
integer name_listener;
integer is_frozen = TRUE;
integer interbreed = FALSE;

Now for our modified running state, which I will break up and add comments in between

state running {
    state_entry() {
        dialog_channel = (integer)llFrand(1000000) - 1000001;
        range_channel = (integer)llFrand(1000000) - 1000001;
        name_channel = (integer)llFrand(1000000) - 1000001;
    }

We’ve added some channels for listening for commands, these channels need to be random to avoid sending commands to multiple pets at once.

    touch_start(integer detected) {
        llSetTimerEvent(60);

        string sick_text;

        if (sickness > 0) {
            sick_text = "I am sick :(";
        } else {
            sick_text = "";
        }

        if (sex == 0) {
            llSetText(llGetObjectName() + "\nSex: Male, Age: " + (string)llFloor(age) + " days\nColour: " + (string)my_colour_trait + "\nHunger: " + hunger + "%\n" + sick_text, <0.384, 0.694, 1.000>, 1.0);
        } else {
            llSetText(llGetObjectName() + "\nSex: Female, Age: " + (string)llFloor(age) + " days\nColour: " + (string)my_colour_trait + "\nHunger: " + hunger + "%\n" + sick_text, <1.000, 0.710, 1.000>, 1.0);
        }

        if (llDetectedKey(0) == llGetOwner()) {
            list dialog_buttons = ["Set Home", "Set Range", "Set Name"];

            if (is_frozen) {
                dialog_buttons = dialog_buttons + "Unfreeze";
            } else {
                dialog_buttons = dialog_buttons + "Freeze";
            }

            if (interbreed) {
                dialog_buttons = dialog_buttons + "Interbreed Off";
            } else {
                dialog_buttons = dialog_buttons + "Interbreed On";
            }
            llListenRemove(dialog_listener);
            dialog_listener = llListen(dialog_channel, "", llGetOwner(), "");
            llDialog(llGetOwner(), "Home Position: " + llLinksetDataRead("home-position") + "\nRange: " + llLinksetDataRead("range") + "\n \nWhat would you like to do?", dialog_buttons, dialog_channel);
        }

Here you will see we check if the person who touched us was the owner, if so, display a dialog box and listen on the dialog channel.

    }

    listen(integer channel, string sender, key id, string message) {
        if (channel == dialog_channel) {
            if (message == "Set Home") {
                llLinksetDataWrite("home-position", (string)llGetPos());
                llOwnerSay("My home position is now: " + llLinksetDataRead("home-position"));
            } else if (message == "Unfreeze") {
                llMessageLinked(LINK_THIS, 10, "", "");
                llOwnerSay("Off I go then!");
                is_frozen = FALSE;
            } else if (message == "Freeze") {
                llMessageLinked(LINK_THIS, 11, "", "");
                llOwnerSay("I will remain very very still!");
                is_frozen = TRUE;
            } else if (message == "Interbreed On") {
                interbreed = TRUE:
                llLinksetDataWrite("interbreeding", (string)interbreed);
            } else if (message == "Interbreed Off") {
                interbreed = FALSE:
                llLinksetDataWrite("interbreeding", (string)interbreed);
            } else if (message == "Set Range") {
                llSetTimerEvent(60);
                llListenRemove(range_listener);
                range_listener = llListen(range_channel, "", llGetOwner(), "");
                llTextBox(llGetOwner(), "Please enter a range between 1.0 and 10.0:", range_channel);
            } else if (message == "Set Name") {
                llSetTimerEvent(60);
                llListenRemove(name_listener);
                name_listener = llListen(name_channel, "", llGetOwner(), "");
                llTextBox(llGetOwner(), "Please enter a new name for me:", name_channel);
            }

Here we respond to the dialog option the owner selects, if it is a command that requires a text box, such as set range or set name, we start listening on those respective channels and reset the timer event (so the owner has time to type in the text box) then display the text box.

        } else if (channel == range_channel) {
            float new_range = (float)llStringTrim(message, STRING_TRIM);

            if (new_range < 1.0) {
                new_range = 1.0;
            } else if (new_range > 10.0) {
                new_range = 10.0;
            }

            llLinksetDataWrite("range", (string)new_range);

            llOwnerSay("My new range is: " + (string)new_range);

If it was the range channel, check the range is within appropriate bounds, then set it. Notice we string trim any whitespace, this is good practice incase someone hits enter in the text box.

        } else if (channel == name_channel) {
            string new_name = llStringTrim(message, STRING_TRIM);

            if (llSubStringIndex(new_name, "^") != -1) {
                llOwnerSay("Sorry, that name has invalid characters!");
            } else if (llStringLength(new_name) <= 1) {
                llOwnerSay("Sorry, that name is too short!");
            } else {
                llSetObjectName(new_name);
                llOwnerSay("I like my new name!");
            }
        }
    }

If it was the name channel, first check the name doesn’t contain our list seperator, if it does it could break any update mechanisms, so we refuse it. Else check if the name is too short - you could allow 1 character names, but 0 character names will break our string lists like having an extra seperator. Finally if it passes those tests set the object name to the name of the pet.

    link_message(integer sender, integer number, string message, key id) {
        if (number == 11) {
            is_frozen = TRUE;
        }
    }

This listens for any frozen link messages from part 3 - remember the pet could set itself to frozen when it is rezzed, we don’t want this script to get out of sync and offer Freeze in the menu instead of Unfreeze if we are already frozen.

    timer() {
        llListenRemove(dialog_listener);
        llListenRemove(name_listener);
        llListenRemove(range_listener);

        llSetTimerEvent(0);
        llSetText("", <1,1,1>, 1.0);
    }

Here we remove the listeners, these commands will silently fail if the listener wasn’t set, so there is no need to add complexity on checking whether they were actually used or not.

    linkset_data(integer action, string name, string value) {
        if (action == LINKSETDATA_UPDATE) {
            if (name == "age") {
                age = (float)value;
            } else if (name == "hunger") {
                hunger = (integer)value;
            } else if (name == "sickness") {
                sickness = (integer)value;
            }
        }
    }
}

That’s essentially our breedable complete.. we just need a way to rez the first egg which we will do in our next part where we modify the egg script into a starter egg script for use in starter eggs.