NPC Exist script

.[npcExist500] series

NPC Exist is a component part of the IruMoto NPC Engine for use in virtual worlds running Open Simulator 9 and later.

Purpose and functionality

The purpose of .[npcExist500] is to create the NPC (or “bot”) and its control menus as per the variables contained in its supporting notecards.

Operational permissions i.e. access to the bots existential functions on the blue menu are limited via llGetCreator() with a reduced menu for select avatars via llSameGroup(). These limits are however only for core functions, e.g. saving outfits, rebooting the NPC etc.

This current series has the name format “.[npcExist600.??]” where ?? is a number then letter, representing the version. The six digit number afterwards (where listed in the version notes) is a date format: YYMMDD.

Variables

Unlike .[npcAction500] in which you must enter your own variables to manage your bot’s behaviour, I strongly recommend dropping .[npcExist500] into your object and leaving it untouched. Instead, change variables via the supporting .npcConfig500.1s notecard, then reboot the script to update its variables.

A note re the state_entry() event as shown in the code snippet below; I have retained the regionOK() and regionDeny() functions, but commented out the latter in order to make this script available to other coders. You can re-enable it for your own worlds if you find it useful, naturally replacing Arius with the name of your own region.

default
{
    state_entry()
    {
        regionOk(); 
//use next bit instead if regionLock required or maybe add a yes/no line to config card. Hard code is probably better as being able to change config card defeats the purpose.
/*        regionLock = llGetRegionName();
        if (regionLock == "Arius") 
            { regionOk(); }
        else if (regionLock != "Arius") 
            { regionDeny(); }*/
    }
}
*/

Other components

Several other scripts, notecards and contents are necessary for the NPC Engine to function. Visit the main IruMoto NPC Engine page for full information.

Code (.[npcExist500.1w])

You may use this script for whatever purpose but must retain the commented header section.

/*
NPC EXIST - IRUMOTO NPC ENGINE
developed by Xay Tomsen in:
InWorldz 2013-14, Reef VR 2015, DigiWorldz 2016-2021.
Special mention to the InWorldz grid monkeys for their brilliant tech of the day and for encouraging this rookie scripter to play with NPCs.
Script purpose and version notes are online at:
http://www.andrewt2020.com/irumoto/npc-engine/npc-exist-script/
---------------------------------------------------
*/
key bot;
key t1;

float f_birthDelay;
vector anglesR; rotation finalRot; rotation rot;

integer gListener;
integer botLive = 0;
integer menuChan;
integer sensorChan;
integer alphaState;

string refreshBotKey;
string usersCard;
string usersMode;
string tempUserCard;
string tempUserUUID;
string tempObjectCard;
string tempObjectUUID;
string anim;
string newMsg;
string changeD;
string saveD;
string emoD;
string usersD;
string couplesD;
string couplesEnterUUIDDia;
string regionLock;

string botPic;
string botProf;
string botNoun;
string botPronoun;

string mainD;
string custMainD;
string savedOutfit;

list saveB;
list changeB;
list mainB;
list custMainB;
list emoB;
list usersB;
list couplesB;

//.npcConfig500.1s
string firstName; string lastName; string botGender; string defO; string botType; string defUUID; string menuChanString; string sensorString; string triggerWord; string birthDelay; string botRevive; string defA;
 
//.botOutfits
string outfitsMain; string outfit1; string outfit2; string outfit3; string outfit4; string outfit5; string outfit6; string outfit7; string outfit8; string outfit9; string outfit10; string outfit11;

endAnim(){
    integer x = 0;
    while(x < llGetInventoryNumber(INVENTORY_ANIMATION)){
        osNpcStopAnimation(bot,llGetInventoryName(INVENTORY_ANIMATION, x++));
    }
    llSleep(2.0);
}

endSceneAnim(){ // used by LOVER bots for jumping between pose balls.
    integer x = 0;
    while(x < llGetInventoryNumber(INVENTORY_ANIMATION)){
        osNpcStopAnimation(bot,llGetInventoryName(INVENTORY_ANIMATION, x++));
    }
    osNpcStand(bot); llSleep(0.5);
}

loadSettings(){
    firstName = osGetNotecardLine(".npcConfig500.1s", 5);
    lastName = osGetNotecardLine(".npcConfig500.1s", 8);
    botGender = osGetNotecardLine(".npcConfig500.1s", 11);
    defO = osGetNotecardLine(".npcConfig500.1s", 14);
    botType = osGetNotecardLine(".npcConfig500.1s", 21);
    defUUID = osGetNotecardLine(".npcConfig500.1s", 24);
    menuChanString = osGetNotecardLine(".npcConfig500.1s", 27);
    sensorString = osGetNotecardLine(".npcConfig500.1s", 30);
    triggerWord = osGetNotecardLine(".npcConfig500.1s", 33);
    birthDelay = osGetNotecardLine(".npcConfig500.1s", 36);
    botRevive = osGetNotecardLine(".npcConfig500.1s", 39);
    defA = osGetNotecardLine(".npcConfig500.1s", 48);
    
    outfit1 = osGetNotecardLine(".botOutfits", 8);
    outfit2 = osGetNotecardLine(".botOutfits", 11);
    outfit3 = osGetNotecardLine(".botOutfits", 14);
    outfit4 = osGetNotecardLine(".botOutfits", 17);
    outfit5 = osGetNotecardLine(".botOutfits", 20);
    outfit6 = osGetNotecardLine(".botOutfits", 23);
    outfit7 = osGetNotecardLine(".botOutfits", 26);
    outfit8 = osGetNotecardLine(".botOutfits", 29);
    outfit9 = osGetNotecardLine(".botOutfits", 32);
    outfit10 = osGetNotecardLine(".botOutfits", 35);
    outfit11 = osGetNotecardLine(".botOutfits", 38);
    
    botPic = osGetNotecardLine(".botProfile500.1s", 1);
    botProf = osGetNotecardLine(".botProfile500.1s", 4);
}

loadGender(){
    if (botGender == "He") { botNoun = "him"; botPronoun = "his"; }
    if (botGender == "She") { botNoun = "her"; botPronoun = "her"; }
    if (botGender == "It") { botNoun = "it"; botPronoun = "its"; }
}

initBot(){
    bot = osNpcCreate(firstName, lastName, llGetPos() + <1.0, 1.0, 1.0>, defO);
    llSetObjectDesc((string)(bot));
    osNpcSetProfileImage(bot, botPic);
    osNpcSetProfileAbout(bot, botProf);
    botLive = 1;
    osNpcSit(bot, defUUID, OS_NPC_SIT_NOW);
    if (botType == "GUEST") { 
        llSleep(0.1); 
        osNpcPlayAnimation(bot, defA); }
    if (botType == "SENSOR") { 
        osNpcStopAnimation(bot, "sit"); 
        llSleep(1.0); }
    if (botType == "ALWAYS") { 
        osNpcStopAnimation(bot, "sit"); 
        llSleep(1.0); }
    if (botType == "TRIGGER") { 
        osNpcStopAnimation(bot, "sit"); 
        llSleep(1.0); }
    llMessageLinked(LINK_THIS, 0, "botLive1", NULL_KEY);
}

loadLists(){
    changeB = [outfit10, outfit11, "★ MAIN", outfit7, outfit8, outfit9, outfit4, outfit5, outfit6, outfit1, outfit2, outfit3];
    saveB = ["s"+outfit10, "s"+outfit11, "★ MAIN", "s"+outfit7, "s"+outfit8, "s"+outfit9, "s"+outfit4, "s"+outfit5, "s"+outfit6, "s"+outfit1, "s"+outfit2, "s"+outfit3];
    emoB = ["CLEAR EMOS", "Worry", "★ MAIN", "Rapt", "Sad", "Sleep", "Grin", "Kiss", "OMG", "Afraid", "Bawl", "Cry"];
    if (botType == "GUEST") {
        usersB = ["Owner", "Anyone", "★ MAIN"];
        couplesB = ["Proceed", "★ MAIN"];
        mainB = ["Save", "Send Home", "Kill Bot", "Reboot", "★ Teleport", "★ Outfits", "★ Emotions"]; 
        custMainB = ["★ Teleport", "★ Outfits", "★ Emotions", "Send Home"]; }
    if (botType == "SENSOR") {
        mainB = ["Save", "★ Outfits", "Rez Bot", "Reboot", "Kill Bot", "★ Emotions"]; 
        custMainB = ["★ MAIN", "Reboot", "★ Outfits", "★ Emotions"]; }
    if (botType == "TRIGGER") {
        mainB = ["Save", "★ Outfits", "Rez Bot", "Reboot", "Kill Bot", "★ Emotions"]; 
        custMainB = ["★ MAIN", "Reboot", "★ Outfits", "★ Emotions"]; }
    if (botType == "ALWAYS") {
        mainB = ["Save", "★ Outfits", "Rez Bot", "Reboot", "Kill Bot", "★ Emotions"]; 
        custMainB = ["★ MAIN", "Reboot", "★ Outfits", "★ Emotions"]; }
}

loadDialog() {
    emoD = "\n★ EMOTIONS: ~ " + firstName + " " + lastName + " ~\n \nUse the emotion buttons below to change " + firstName + "'s facial expression.\n \nTo use this feature properly, click an Emotion button once to turn it on, then the same button again to turn it off.\n \nIf you do happen to lose track, use the CLEAR EMOS button as a final resort. It may take up to 10 seconds to clear them all.";
    saveD = "\n★ SAVE OUTFITS: ~ " + firstName + " " + lastName + " ~\n \nWhich outfit would you like to replace?";
    changeD = "\n★ CHANGE OUTFITS: ~ " + firstName + " " + lastName + " ~\n \nSelect an appearance for " + firstName + ":";
    custMainD = "\n★ MAIN MENU: ~ " + firstName + " " + lastName + " ~\n \nThis is the visitor control panel.\n \nTo interact with " + firstName + ", select an option below:";
    if (botType == "ALWAYS") {
        mainD = "\n★ MAIN MENU: ~ " + firstName + " " + lastName + " ~\n \nThis is the main control panel.\n \nTo control " + firstName + ", select an option below:"; }
    if (botType == "SENSOR") {
        mainD = "\n★ MAIN MENU: ~ " + firstName + " " + lastName + " ~\n \nThis is the main control panel.\n \nTo control " + firstName + ", select an option below:"; }
    if (botType == "TRIGGER") {
        mainD = "\n★ MAIN MENU: ~ " + firstName + " " + lastName + " ~\n \nThis is the main control panel.\n \nTo control " + firstName + ", select an option below:"; }
    if (botType == "GUEST") {
        mainD = "\n★ MAIN MENU: ~ " + firstName + " " + lastName + " ~\n \nThis is the main menu for " + firstName + ", where you can control " + botPronoun + " actions, appearance, and emotions.\n \n" + firstName + " is designed for private landholders in Irukandji Estate for use on land that is held in their name.\n \n" + firstName + " will keep you company for one hour from the time this menu opened.\n \nNOTE: " + firstName + " is locked to your own property on the Irukandji Estate. " + botGender + " will crash if teleported beyond your parcel or to any other region.";
        usersD = "\n★ USER PERMISSIONS: ~ " + firstName + " " + lastName + " ~\n \nWhich setting would you like to apply to this IruMoto bot unit?\n \n- Owner: means only you.\n- Group: means any members of your active group.\n- Anyone: means absolutely anyone.\n \nNote that some menus, such as saving outfits and setting user permissions, can only be accessed by the owner.";
        couplesD = "\n★ TELEPORT: ~ " + firstName + " " + lastName + " ~ \n \n" + firstName + " can teleport to any poseball on your parcel of land. You will need the UUID of " + botPronoun + " intended poseball.\n \nIn Firestorm, right-click any poseball, choose Edit. In the Edit window, click on 'Copy Keys', then on this menu, click 'Proceed'.\n \nNOTE: When a multi-player animator rezzes a new ball, e.g. when you restart or stop it, that ball is given a new UUID. You will need to teleport " + firstName + " again so " + botGender + " can recognise the new ball.";
        couplesEnterUUIDDia = "\n♥ ENTER POSEBALL UUID: ~ " + firstName + " " + lastName + " ~ \n \nPaste the UUID of the poseball that " + firstName + " will use, then click the Submit button."; }
}

saveO() {
    osOwnerSaveAppearance(savedOutfit);
    llOwnerSay("You have saved your current appearance as " + savedOutfit + " for " + firstName + " to notecard " + savedOutfit + ".");
}

regionDeny(){
    llSay(0, "This device is locked to Arius region."); }    

regionOk(){
    llSitTarget(<0.0, 0.0, 0.15>, ZERO_ROTATION);
    loadSettings();
    loadGender();
    loadLists();
    loadDialog();
    menuChan = (integer)(menuChanString);
    sensorChan = (integer)(sensorString);
    f_birthDelay = (float)(birthDelay);
    if (botType == "SENSOR") {
        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 0]);
        alphaState = 0;
        state ready; }
    if (botType == "TRIGGER") {
        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 0]);
        alphaState = 0; 
        state ready; }
    if (botType == "GUEST") {
        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 1]);
        alphaState = 1; 
        state ready; }
    if (botType == "ALWAYS") {
        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 0]);
        alphaState = 1; 
        state ready; }
}

notices() {
    llSay(0, "Please wait a moment while " + firstName + " " + lastName + " comes online.");
    llInstantMessage(llGetCreator(), firstName + " " + lastName + " is attending " + llKey2Name(t1));
}

botSleep() {
    llWhisper(0, firstName + " " + lastName + " has timed out. You can re-start "+ firstName +" if you wish to spend more time with "+ botNoun +".");
    llSleep(5.0);
    llResetScript();
}

default
{
    state_entry()
    {
        regionOk(); 
//use next bit instead if regionLock required or maybe add a yes/no line to config card. Hard code is probably better as being able to change config card defeats the purpose.
/*        regionLock = llGetRegionName();
        if (regionLock == "Arius") 
            { regionOk(); }
        else if (regionLock != "Arius") 
            { regionDeny(); }*/
    }
}

state ready
{    
    state_entry() {
        llListen(menuChan, "", NULL_KEY, "");
        llListen(sensorChan, "", NULL_KEY, "");
        if (botType == "ALWAYS") { 
            llSleep(f_birthDelay);
            initBot();
            llSetTimerEvent(3600);
        }
    }
    
    on_rez(integer start_param){
        llResetScript();
    }
    
    timer(){
        if (botLive == 1 && botType == "TRIGGER") {
            llRegionSay(sensorChan, triggerWord + "Restore");
            osNpcRemove(bot);
            botSleep();
        }
        if (botLive == 1 && botType == "GUEST") {
            osNpcRemove(bot);
            botSleep();
        }
        if (botLive == 1 && botType == "ALWAYS") {
            osNpcRemove(bot);
            botSleep();
        }
    }
    changed(integer change)
    {
        if (change & CHANGED_OWNER)
        {
            llResetScript();
        }
        if (change & CHANGED_REGION_START)
        {
            llSleep(60);
            llResetScript();
        }
        if (change & CHANGED_LINK)
        {
            if (botType == "SENSOR") {
                if (llAvatarOnSitTarget() != NULL_KEY)
                {
                    llRequestPermissions(llAvatarOnSitTarget(), PERMISSION_TRIGGER_ANIMATION);
                }
                else 
                {
                    integer perm=llGetPermissions();
                    if (perm & PERMISSION_TRIGGER_ANIMATION) {
                        osNpcStopAnimation(bot, anim);
                    }
                }
            }
            if (botType == "TRIGGER") {
                if (llAvatarOnSitTarget() != NULL_KEY)
                {
                    llRequestPermissions(llAvatarOnSitTarget(), PERMISSION_TRIGGER_ANIMATION);
                }
                else 
                {
                    integer perm=llGetPermissions();
                    if (perm & PERMISSION_TRIGGER_ANIMATION) {
                        osNpcStopAnimation(bot, anim);
                    }
                }
            }
            if (botType == "ALWAYS") {
                if (llAvatarOnSitTarget() != NULL_KEY)
                {
                    llRequestPermissions(llAvatarOnSitTarget(), PERMISSION_TRIGGER_ANIMATION);
                }
                else 
                {
                    integer perm=llGetPermissions();
                    if (perm & PERMISSION_TRIGGER_ANIMATION) {
                        osNpcStopAnimation(bot, anim);
                    }
                }
            }
        }
    }
    
    run_time_permissions(integer perm)
    {
        if (perm & PERMISSION_TRIGGER_ANIMATION)
        {
            osNpcStopAnimation(bot, "sit");
            anim = llGetAnimation(llAvatarOnSitTarget());
            llStartAnimation(anim);
        }
    }
    
    touch_start(integer total_num) {
        t1 = llDetectedKey(0);
        menuChan = (integer)(menuChanString);
        if (botType == "GUEST") {
            if (botLive == 1) {}
            else if (botLive == 0) {
                llSleep(f_birthDelay);
                initBot();
                notices();
                llSetTimerEvent((integer)(botRevive)); }
        }
        if (t1 == llGetCreator()) {
            llDialog(t1, mainD, mainB, menuChan); }

        if (t1 != llGetCreator()) {
            if (t1 == llSameGroup(t1)) {
                llDialog(t1, custMainD, custMainB, menuChan);
                }
            else {
                osNpcSay(bot,"Please activate the correct group to access my menu."); }
        }
    }
    
    listen(integer chan, string name, key id, string msg)
    {
        if (chan == sensorChan) {
            if (msg == "visitor0") { 
                if (botType == "TRIGGER") {
                    if (botLive == 1) {
                        endAnim(); osNpcRemove(bot); botLive = 0;
                        llMessageLinked(LINK_THIS, 0, "resetAll", NULL_KEY);
                    }
                    else if (botLive == 0) {}
                }
                else if (botType == "SENSOR") {
                    if (botLive == 1) {
                        endAnim(); osNpcRemove(bot); botLive = 0;
                        llMessageLinked(LINK_THIS, 0, "resetAll", NULL_KEY);
                    }
                    else if (botLive == 0) {}
                }
            }
            else if (msg == "visitor1") { 
                if (botType == "SENSOR") {
                    if (botLive == 0) {
                        llSleep(f_birthDelay);
                        initBot();
                    }
                }
            }
            else if (msg == triggerWord + "End") {
                if (botType == "TRIGGER") {
                    if (botLive == 1) {
                        osNpcRemove(bot);
                        botSleep();
                    }
                }
            }
                    
            else if (msg == triggerWord) {
                if (botType == "TRIGGER") {
                    if (botLive == 0) {
                        llSleep(f_birthDelay);
                        initBot();
                        notices();
                        llSetTimerEvent(1800);
                    }
                }
            }
        }
        if (chan == menuChan) {
            if (msg == "★ MAIN" && t1 == llGetCreator()) {
                llDialog(t1, mainD, mainB, menuChan);
            }
            else if (msg == "★ MAIN" && t1 != llGetCreator()) {
                llDialog(t1, custMainD, custMainB, menuChan);
            }
            else if(msg == "★ Teleport"){
                llDialog(t1, couplesD, couplesB, menuChan);
            }
            else if(msg == "Proceed"){
                string tempUserCard = ".tempUserSettings";
                llRemoveInventory(tempUserCard);
                list tempUserCardSettings;
                tempUserCardSettings += [t1];
                osMakeNotecard(tempUserCard,tempUserCardSettings);
                llInstantMessage(t1, "Please have your pose ball UUID ready to paste in the text box.");
                state proceed;
            }
            else if(msg == "Alpha") {
                if (botType == "ALWAYS") {
                    if (alphaState == 0) {
                        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 0.4]);
                        alphaState = 1;}
                    else if (alphaState == 1) {
                        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 0.0]);
                        alphaState = 0; }
                }
                if (botType == "SENSOR") {
                    if (alphaState == 0) {
                        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 0.4]);
                        alphaState = 1;}
                    else if (alphaState == 1) {
                        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 0.0]);
                        alphaState = 0; }
                }
                if (botType == "TRIGGER") {
                    if (alphaState == 0) {
                        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 0.4]);
                        alphaState = 1;}
                    else if (alphaState == 1) {
                        llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 0.0]);
                        alphaState = 0; }
                }
            }
            else if (msg == "Rez Bot") {
                if (botLive == 0) { initBot();
                llDialog(t1, mainD, mainB, menuChan); }
            }
            else if (msg == "Kill Bot") {
                osNpcRemove(bot);
                llDialog(t1, mainD, mainB, menuChan);
                llMessageLinked(LINK_THIS, 0, "botLive0", NULL_KEY);
                if (botType == "GUEST") { textIdle;}
            }
            else if (msg == "Reboot") {
                osNpcRemove(bot);
                llMessageLinked(LINK_THIS, 0, "resetAll", NULL_KEY);
                llResetScript();
            }
            else if (msg == "Save") { llDialog(t1, saveD, saveB, menuChan);
            }
            else if (msg == "★ Outfits") { llDialog(t1, changeD, changeB, menuChan);
            }

            else if(msg == "Send Home"){
                osNpcRemove(bot);
                botLive = 0;
                llResetScript();
            }

//Emotions - communicates with npcEmotions script
            else if(msg == "★ Emotions"){
                llDialog(t1, emoD, emoB, chan);
            }
            else if(msg == "Afraid") {
                llMessageLinked(LINK_THIS, 0, "Afraid", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "Bawl") {
                llMessageLinked(LINK_THIS, 0, "Bawl", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "Cry") {
                llMessageLinked(LINK_THIS, 0, "Cry", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "Grin") {
                llMessageLinked(LINK_THIS, 0, "Grin", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "Kiss") {
                llMessageLinked(LINK_THIS, 0, "Kiss", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "OMG") {
                llMessageLinked(LINK_THIS, 0, "OMG", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "Rapt") {
                llMessageLinked(LINK_THIS, 0, "Rapt", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "Sad") {
                llMessageLinked(LINK_THIS, 0, "Sad", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "Sleep") {
                llMessageLinked(LINK_THIS, 0, "Sleep", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "Worry") {
                llMessageLinked(LINK_THIS, 0, "Worry", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }
            else if(msg == "CLEAR EMOS") {
                llMessageLinked(LINK_THIS, 0, "ClearEmos", NULL_KEY);
                llDialog(t1, emoD, emoB, chan); }

//Save Outfits
            else if (msg == "s"+outfit1) {
                osOwnerSaveAppearance(".outfit1");
                llOwnerSay("Saved " + outfit1 + " for " + firstName + " to notecard .outfit1.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit2) {
                osOwnerSaveAppearance(".outfit2");
                llOwnerSay("Saved " + outfit2 + " for " + firstName + " to notecard .outfit2.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit3) {
                osOwnerSaveAppearance(".outfit3");
                llOwnerSay("Saved " + outfit3 + " for " + firstName + " to notecard .outfit3.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit4) {
                osOwnerSaveAppearance(".outfit4");
                llOwnerSay("Saved " + outfit4 + " for " + firstName + " to notecard .outfit4.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit5) {
                osOwnerSaveAppearance(".outfit5");
                llOwnerSay("Saved " + outfit5 + " for " + firstName + " to notecard .outfit5.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit6) {
                osOwnerSaveAppearance(".outfit6");
                llOwnerSay("Saved " + outfit6 + " for " + firstName + " to notecard .outfit6.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit7) {
                osOwnerSaveAppearance(".outfit7");
                llOwnerSay("Saved " + outfit7 + " for " + firstName + " to notecard .outfit7.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit8) {
                osOwnerSaveAppearance(".outfit8");
                llOwnerSay("Saved " + outfit8 + " for " + firstName + " to notecard .outfit8.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit9) {
                osOwnerSaveAppearance(".outfit9");
                llOwnerSay("Saved " + outfit9 + " for " + firstName + " to notecard .outfit9.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit10) {
                osOwnerSaveAppearance(".outfit10");
                llOwnerSay("Saved " + outfit10 + " for " + firstName + " to notecard .outfit10.");
                llDialog(t1, saveD, saveB, menuChan);
            }
            else if(msg == "s"+outfit11) {
                osOwnerSaveAppearance(".outfit11");
                llOwnerSay("Saved " + outfit11 + " for " + firstName + " to notecard .outfit11.");
                llDialog(t1, saveD, saveB, menuChan);
            }
    
//Change Outfits
            else if(msg == outfit1) {
                osNpcLoadAppearance(bot, ".outfit1");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit2) {
                osNpcLoadAppearance(bot, ".outfit2");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit3){
                osNpcLoadAppearance(bot, ".outfit3");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit4){
                osNpcLoadAppearance(bot, ".outfit4");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit5){
                osNpcLoadAppearance(bot, ".outfit5");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit6){
                osNpcLoadAppearance(bot, ".outfit6");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit7){
                osNpcLoadAppearance(bot, ".outfit7");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit8){
                osNpcLoadAppearance(bot, ".outfit8");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit9) {
                osNpcLoadAppearance(bot, ".outfit9");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit10) {
                osNpcLoadAppearance(bot, ".outfit10");
                llDialog(t1, changeD, changeB, menuChan);
            }
            else if(msg == outfit11) {
                osNpcLoadAppearance(bot, ".outfit11");
                llDialog(t1, changeD, changeB, menuChan);
            }
        }
    }
}

state proceed {
    state_entry() {
        tempUserUUID = osGetNotecardLine(".tempUserSettings", 0);
        gListener = llListen(menuChan, "", "", "");     
        llTextBox(tempUserUUID, couplesEnterUUIDDia, menuChan);
    }
    listen(integer channel, string name, key tempUserUUID, string tempObjectUUID)
    {
        llListenRemove(gListener);
        string tempObjectCard = ".tempObjectSettings";
        llRemoveInventory(tempObjectCard);
        list tempObjectCardSettings;
        tempObjectCardSettings += [tempObjectUUID];
        osMakeNotecard(tempObjectCard,tempObjectCardSettings);
        llInstantMessage(tempUserUUID, firstName + " is teleporting now.");

        endSceneAnim();
        osNpcSit(bot, tempObjectUUID, OS_NPC_SIT_NOW);
        
        t1 = tempUserUUID;
        if (t1 == llGetCreator()) {
            llDialog(t1, mainD, mainB, menuChan); }

        if (t1 != llGetCreator()) {
            llDialog(t1, custMainD, custMainB, menuChan);
        }
        
        state ready;
    }
}

Version Notes

2023

v600 230326

  • reverts to 11 outfits as any more is superfluous
  • also exacerbated load errors for Build Tools > Contents tab on var-regions
  • added link_message function for reverse comms from other scripts
  • now handles outfit changes as instructed by npcAction
  • changed references to ‘outfit’ to ‘o’
  • removed unused legacy saveOutfit function and strings
  • replaced repetitious calls in Save Outfit messages with saveO function
  • .botOutfits500.2d notecard changes to .npcOutfits600
  • requires .npcConfig600 notecard

2021

  • v500.2e 211230 – added extra detail to ping response to specify botType.
  • v500.2d 211215 – increased outfits count from 11 to 20. Requires .[npcAction500.1r], .botOutfits500.2d, plus 9 extra .outfit notecards.
  • v500.2c 211206 – resetAll from prev update only resets npcAction.
  • Added llResetScript() to tgDeath so npcExist resets as well.
  • botlive = 0 removed as it gets picked up in the reset anyway.

v500.2b 211203 – for TIMEGEN bots, added empty else if statements to listen events for botLive states for tgBirth and tgDeath, also endAnim and resetAll to tgDeath (copied from TRIGGER).

v500.2a 211124 – NPCs began crashing. Cause turned out to be some of the || parameters in various functions from my tidying up code yesterday. Reverted, remembering that this happened a few years ago too. All good now.

v500.1z 211124 – added “ping” functionality, initiated by TimeGen 4.2. The purpose of this feature is to keep track of all IruMoto NPCs on the region. By touching the TimeGen, you are prompted to ping the NPCs, which then return IMs of their names and locations. Responses are staggered using the existing f_birthDelay. Required opening up tgChan listen to all botTypes in .npcExist but not in .npcAction. This keeps the functions of existence vs action distinct.

  • v500.1y 211124 – tidied up several if/else statements with || operators.
  • Cleaned up about 80 lines of code.

v500.1x 211123 re-jig scripts to allow new TimeGen4 to act as an NPC controller. Requires new notecard .npcConfig500.1x

v500.1w 210828 – since .1v, on initial startup, bots are getting stuck between stop sit anim and defA which is a sequence prob between this script and npcActions. Have combined initBot() and initDefaults() in order to rearrange sequence of stuff, and make a later call to botLive which then triggers npcAction.

v500.1v 210827 – moving llSetObjectDesc into initBot() so that this happens before the botLive call to other scripts, thus eliminating the need for llSleep() calls in .npcAction, which hopefully will eradicate the long-running and unpredictable off-world glitch on reset.

v500.1u 210809 – commented out regionLock() as it’s rarely used and can be added to the config card at some future point. regionLock was causing issues in local grid, as Arius is irrelevant. This script is the result and works offline.

  • v500.1t 210731 – Narrowed regionLock() to Arius.
  • Added notices() to streamline code including reword of message to user.
  • Added botSleep() to tidy code for suicide.

V500.1s 210721 – removing a lot of empty if statements, partic in regard to VENUE and GUEST bots. I can’t see a need for VENUE bots anymore, which sit on a poseball the same as GUEST NPCs, but I don’t think I’ll ever use this function again and can always morph the GUEST code if I do, so am deleting it. Also adding a suffix to .botProfile500.1s and .npcConfig500.1s cards as I want them uniform.

v500.1r 210721 – implemented osNpcSetProfileAbout and osNpcSetProfileImage. Both contained in initBot(). Also required changes to botProfile notecard layout and how it is read.

v500.1q 210706 – in initBot, changed rez location to 1,1,1 to see if the cause of NPC rezzing in the region corner is due to the script sometimes failing to do the llGetPos call beforehand. The recent mod to .[npcAction500.1k] didn’t fix the prob.

v500.1p 210109 – changed regionLock from Arius to Aboyo Beach. Also re-added ALWAYS functionality for prop NPCs. Latter required parallel update to .[npcAction500.1h].

v500.1o 210106 – approx line 378 added group ability to if/else llGetCreator statements so land owners can access secondary menu.

2019

v500.1n 190507 – changed default colours of VENUE & GUEST bots to white. Also removed hovertext.

v500.1m – 190504 – in initBot(), added decimals to pos vector to see if float syntax is the problem causing bots to crash approx every tenth time on startup. Possibly noteworthy is that older scripts e.g. v3.78 have the same issue. As far back as v2.03 also lack a decimal but I can’t recall if this was ever an issue. Probably not as they weren’t triggered by a sensor and I suspect there is some correlation there. NOTE 190507 NO DIFFERENCE. STILL CRASHES EVERY 10TH OR SO TIME.

v500.1k – 190206 – replaced saveO() function with 2 lines of code in Save Outfits menu (from v3.6) to re-add outfit description to chat as double check I hit the right button.

v500.1j – corrects a new (and never happened before) script warning with more than 512 characters in Emotions dialogue.

v500.1i – reverted to 2 kinds of lover NPC: VENUE and GUEST. Necessary for land permissions and possibly different menus later on. Also modified menus using llGetCreator instead of llGetOwner.

v500.1h – tidied up LOVER & ESCORT dialog, menu, instructions & textXXX() functions. IM upon use changed from llGetOwner to llGetCreator to track usage if/when I supply unit as a HUD. Combine LOVER & ESCORT bots into GUEST. Removed redundant code for ALWAYS bots.

V500.1g – added regionLock to stop people pinching the bot for their own region.

v500.1f – made menu automatically pop up after state proceed.

v500.1e – replaced SUCK emotion with CLEAR EMOS.

v500.1d – Replacing ALWAYS bots with ESCORT bots. Timer() seems to be preventing Emotions script working with ALWAYS bots, which is a function I want. Also having ALWAYS bots constantly on defeats the purpose of trying to prevent script crashes inherent with the old v3.04 series. Hence, added ESCORT botType which is a blend of TRIGGER to activate them, with LOVER functionality. redundant ALWAYS sections retained for legacy.

2018

v500.1c – corrected auto rebooting issues with ALWAYS bots – was 10 secs instead of botRevive, also added Timer() even to default to set it up.

v500.1b – added several ALWAYS functions and if statements.

v500.1a – fixed another multiple || issue which affected menus for LOVER bots.

v500.1 fixed an issue with Trigger bots responding like Sensor bots. Rearranged the order of if/else statements in listen() which is a problem that goes WAY back into the earlier codes but for some reason didn’t play up. Hopefully all sorted now. The listen() also doesn’t like multiple || in the if/else statements. Confuses it.

v500 – successfully combined the old 200, 300, and 400 series bots. One code set now does all so I’ve rebadged them all 500 onwards.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.