User:Allen Kerensky/Myriad Lite Preview4/BAM Adventure Giver NPC v0.0.6 20110906

From Second Life Wiki
Jump to navigation Jump to search

BAM Adventure Giver NPC v0.0.6 20110906

<lsl> // Baroun's Adventure Machine (BAM) Adventure Giver NPC v0.0.6 20110906 // Copyright (c) 2008-2011 Baroun Tardis (SL) and Allen Kerensky (OSG/SL) // Baroun's Adventure Machine licensed under the // Creative Commons Attribution-Share Alike-Non-Commercial 3.0 Unported // http://creativecommons.org/licenses/by-nc-sa/3.0/

//============================================================================ // Adventure-Specific Configuration //============================================================================ // NPC-specific Info string MSG_NPCNAME = "BAM Adventure Giver NPC"; // hovertext name for NPC - I hate hovertext vector MSG_SETTEXT_COLOR = <1.0,1.0,1.0>; // color to display settext in float MSG_SETTEXT_ALPHA = 1.0; // alpha for hovertext. 0.0 = clear, 1.0 = solid.

// Adventure-specific Info string ADVNAME = "Red Salt"; // Adventure Name string ADVTEXT = "Find some red salt for luck."; // brief description string ADV_ATTRACT = "can you help me find some red salt?"; // hook to get player to play string MSG_OWNER_STARTADV = " has started Red Salt Adventure"; string ADV_ALL_TASKS = "101, 102"; // CSV list of task numbers that all have to be done for adventure to be complete example: "101, 102, 103" string MSG_ADV_INCOMPLETE = ", it looks like you still haven't found the red salt for me."; // prefixed by AVNAME string MSG_OWNER_DONEADV = " has completed the Red Salt Adventure"; // message to speak when task complete string ADVDONETEXT = "Thanks! I appreciate your help!"; string ADVDONEUUID = "f78027c9-e8bb-38f2-9b11-1d4e89ac10a4"; //object or sounds to give as a mission complete string PRIZENAME = "NONE"; // prize to give when adventure is complete

// Task-Specific Info // Task numbers are (AdvNum*100)+task, so they don't match up between adventures integer ADVTASKTDNUM = 101; // task number for the task THIS node hands out string ADVTASKTODO = "Find the Red Salt Mines"; // task description of what to do string ADVTASKTODOHINT = "Look in the other corner of the room"; // string

integer TRIGGERWAIT = 60; // seconds to remember players to prevent re-triggering task? integer PRIZEWAIT = 3600; // seconds to remember players who got prize? float EVENTTIMER = 15.0; // seconds between running memory and list cleanup timed events float SENSOR_RANGE = 3.0; // how close do you have to be to trigger the NPC float SENSOR_ARC = PI; // what is the sensor's sweep? float SENSOR_REPEAT = 10; // how often to repeat the sensor

//============================================================================ // MESSAGE FORMAT REFERENCE //============================================================================ // -1 OUT - [YES,NO,CLOSE] // -1 IN - YES // BAMCHAN OUT - InAdv? // BAMCHAN IN - InAdv|str NONE // BAMCHAN IN - InAdv|str AdventureName // BAMCHAN OUT - OfferAdv|str AdventureName|str AdventureText // BAMCHAN OUT - TaskCP? // BAMCHAN IN - AcceptAdv|str AdvName // BAMCHAN OUT - AddTask|int TaskNumber|str TaskToDo // BAMCHAN OUT - AddHint|int TaskNumber|str TaskHint // BAMCHAN IN - TaskCP| // BAMCHAN IN - TaskCP|str 999 // BAMCHAN OUT - DoneAdv|str AdventureName|str AdventureText|key AdventureDoneUUID

//============================================================================ // Global Constants //============================================================================ string CHAN_PREFIX = "0x"; // random chat channel prefix string CHAT_DIVIDER = ", "; // to comma-separate elements in emitted chat elements integer DEBUG_FLAG = FALSE; // set to true for debug messages string MSG_STARTUP = "Baroun's Adventure Machine is activating."; string DEBUG_LISTEN_CHANNEL= "BAM Listening on channel: "; // debug message to see picked channel string API_INADV_QUERY = "InAdv?"; // API trigger to check if in an adventure. float API_INADV_TIMEOUT = 3.0; // API timeout for an InAdv? request string API_TASKCP_QUERY = "TaskCP?"; // task complete? string API_INADV_RESPONSE = "InAdv"; // confirms player is already in an adventure string DIV1 = "|"; // divides fields of API messages string API_NONE = "NONE"; // magic text if player not in an adventure string API_OFFER_ADV = "OfferAdv"; // offer player an adventure string API_ACCEPT_ADV = "AcceptAdv"; // player accepts adventure string API_ADD_TASK = "AddTask"; // add a task to player string API_ADD_HINT = "AddHint"; // add a hint for the current task string API_TASKCP_RESPONSE = "TaskCP"; // task completed string API_DONEADV = "DoneAdv"; // adventure done

//============================================================================ // Runtime Globals //============================================================================ list Recent; // list of [UUID,unixtime] who recently collided with this goal list GotPrizes; // list of who got prizes [UUID,unixtime] key AVKEY; // UUID of the avatar we are interacting with string AVNAME; // String name of the avatar we are interacting with integer CHANBAM; // Channel we listen on integer CHANTARGET; // channel of thing we're talking to

//============================================================================ // DEFAULT STATE //============================================================================ default {

   //------------------------------------------------------------------------
   // STATE_ENTRY EVENT
   //------------------------------------------------------------------------
   state_entry() {
       llParticleSystem([]); // shut off any running particles
       llSetText(MSG_NPCNAME,MSG_SETTEXT_COLOR,MSG_SETTEXT_ALPHA);
       llOwnerSay(MSG_STARTUP); // tell the owner we are initializing
       CHANBAM = (integer)(CHAN_PREFIX + llGetSubString((string)llGetKey(),-7,-1)); // calculate channel to listen on
       if (DEBUG_FLAG) llOwnerSay(DEBUG_LISTEN_CHANNEL+(string)CHANBAM);
       llListen(CHANBAM,"",NULL_KEY,""); // listen on channel for all messages from any name, name UUID, and any message
       llSetTimerEvent(EVENTTIMER); // fire off a timer event once an hour
       llSensorRepeat("",NULL_KEY,AGENT,SENSOR_RANGE,SENSOR_ARC,SENSOR_REPEAT); // start NPC looking for someone to help
   }
   //------------------------------------------------------------------------
   // TOUCH_START EVENT
   //------------------------------------------------------------------------
   sensor(integer num_sensed) {
       while (num_sensed--) { // count down through all touches in this event
           AVKEY=llDetectedKey(num_sensed); // get the UUID of the toucher
           if ( llListFindList(Recent,[AVKEY]) == -1 ) { // player not in list of people recently completing the quest
               AVNAME=llDetectedName(num_sensed); // get the name of the toucher
               CHANTARGET = (integer)(CHAN_PREFIX + llGetSubString((string)AVKEY,-7,-1));
               llSay(CHANTARGET, API_INADV_QUERY); // ask player if they are in adventure, must be within 10m for effect
           }
       }
   }
   //------------------------------------------------------------------------
   // TIMER EVENT
   //------------------------------------------------------------------------
   timer() {
       // on timer, check memory left and clear recent list if needed
       integer freemem = llGetFreeMemory(); // how much memory free?
       if ( freemem < 1024 ) { // is it too little?
           llInstantMessage(llGetOwnerKey(llGetKey()),"Memory low for "+llGetObjectName()+" in "+llGetRegionName()+". Resetting RECENT list.");
           Recent=[]; // clear the recent list
           GotPrizes=[]; // clear the gotPrizes list
           return; // exit timer event, no sense in processing lists further since we just emptied them
       }
       // check to see if entries in Recent list have expired
       integer i; // temporary index number into list
       list temprecent = []; // temporary list to hold entries we want to keep
       key who; // temporary place to keep the keys we process in the lists
       integer time; // temporary place to keep the time we process in the lists
       for (i = 0; i < llGetListLength(Recent); i += 2) { // step through strided list from begin to end
           who = llList2Key(Recent,i); // get the UUID for this list stride
           time = llList2Integer(Recent,i+1); // get the integer time for this list stride
           if ( llGetUnixTime() < time ) temprecent = [who,time] + temprecent; // non expired, keep this entry
       }
       Recent = temprecent; // now, replace the Recent list with the pruned version
       // check to see if entries in GotPrizes list have expired
       temprecent = []; // clear the temp list again
       for (i = 0; i < llGetListLength(GotPrizes); i += 2) { // step through next strided list
           who = llList2Key(GotPrizes,i); // get the uuid for this list stride
           time = llList2Integer(GotPrizes,i+1); // get the integer time for this list stride
           if ( llGetUnixTime() < time ) temprecent = [who,time] + temprecent; // non expired, keep this entry
       }
       GotPrizes = temprecent; // replace the gotprizes list with the pruned one           
   }
   //------------------------------------------------------------------------
   // LISTEN EVENT
   //------------------------------------------------------------------------
   listen(integer chan, string name, key id, string msg) {
       list   tokens  = llParseString2List(msg, [DIV1], []); // split incoming message apart using |
       string command = llList2String(tokens, 0); // the first part of the message is a command
       string data    = llList2String(tokens, 1); // the second part is command-specific data
       // if they answer they are in an adventure, react accordingly
       if ( command == API_INADV_RESPONSE ) { // In An Adventure Response
           if ( data == API_NONE && llListFindList(Recent,[llGetOwnerKey(id)]) == -1 ) { // responded with no current adventure
               llSay(PUBLIC_CHANNEL,AVNAME+CHAT_DIVIDER+ADV_ATTRACT);
               llSay(CHANTARGET,API_OFFER_ADV+DIV1+ADVNAME+DIV1+ADVTEXT); // offer one
               return;        
           }
           if ( data == ADVNAME ) { // already in the current adventure
               llSay(CHANTARGET,API_TASKCP_QUERY); // so let's check if they have completed adventure?
               return;
           }
       }
       // If they are accepting the adventure, hand them first task
       if ( command == API_ACCEPT_ADV ) { // accepting an adventure
           if ( data == ADVNAME ) { // accepting this nodes adventure
               llSay(PUBLIC_CHANNEL,ADVTEXT);
               llSay(CHANTARGET,API_ADD_TASK+DIV1+(string)ADVTASKTDNUM+DIV1+ADVTASKTODO); // give them first task
               llSay(CHANTARGET,API_ADD_HINT+DIV1+(string)ADVTASKTDNUM+DIV1+ADVTASKTODOHINT); // give them first task hint
               llInstantMessage(llGetOwner(),llKey2Name(llGetOwnerKey(id))+MSG_OWNER_STARTADV); // tell owner adventure begins
               return; // done with this chat command, exit early
           }
       }
       if ( command == API_TASKCP_RESPONSE ) { // player sends TaskCP|task# to NPC    
           string playertasks = llList2CSV(llListSort(llCSV2List(data),1,TRUE));
           string alltasks = llList2CSV(llListSort(llCSV2List(ADV_ALL_TASKS),1,TRUE));
           if ( playertasks == alltasks ) { // if all tasks are in done list, then adventure is done
               Recent = [ llGetOwnerKey(id), llGetUnixTime() + TRIGGERWAIT ] + Recent; // remember this player is done
               // tell player HUD that the adventure is done
               llSay(CHANTARGET,API_DONEADV+DIV1+ADVNAME+DIV1+ADVDONETEXT+DIV1+ADVDONEUUID);
               // tell player and public that adventure is done
               llSay(PUBLIC_CHANNEL,llKey2Name(llGetOwnerKey(id))+CHAT_DIVIDER+ADVDONETEXT);
               // give player the prize if one is defined
               if (PRIZENAME!="NONE"  && llListFindList(GotPrizes,[llGetOwnerKey(id)]) == -1 ) {
                   llGiveInventory(llGetOwnerKey(id),PRIZENAME);
                   GotPrizes = [ llGetOwnerKey(id), (llGetUnixTime() + PRIZEWAIT) ] + GotPrizes; // remember who and when
               }
               // send message to quest OWNER that a player finished and got the prize
               llInstantMessage(llGetOwner(),llKey2Name(llGetOwnerKey(id))+MSG_OWNER_DONEADV);
               return; // exit early
           }
           // player has not completed last quest task, so tell them there is more to do
           llSay(PUBLIC_CHANNEL,llKey2Name(llList2String(llGetObjectDetails(id,[OBJECT_OWNER]),0))+MSG_ADV_INCOMPLETE);
           return; // exit early in case we add more chat commands
       }
   }

} //============================================================================ // END //============================================================================ </lsl>