BAM_Location_Goal-v0.0.9-20120704.lsl
// Baroun's Adventure Machine BAM_Touch_Goal-v0.0.9-20120704.lsl
// Copyright (c) 2008-2012 Baroun Tardis (SL) and Allen Kerensky (OSG/SL)
// The Myriad RPG System was designed, written, and illustrated by Ashok Desai
// Myriad RPG licensed under the Creative Commons Attribution 2.0 UK: England and Wales
// http://creativecommons.org/licenses/by/2.0/uk/
// Myriad Lite software Copyright (c) 2011-2012 by Allen Kerensky (OSG/SL)
// Baroun's Adventure Machine Copyright (c) 2008-2011 by Baroun Tardis (SL)
// Myriad Lite and 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/
// You must agree to the terms of this license before making any use of this software.
// If you do not agree to this license, simply delete these materials.
// There is no warranty, express or implied, for your use of these materials.
//============================================================================
// Adventure-Specific Configuration
// Task numbers are (AdvNum*100)+task, so they don't overlap between adventures
//============================================================================
// NPC or object-specific info
// Adventure-specific info
string ADVNAME = "Red Salt"; // Adventure Name
// Current Task-specific info
integer ADVGOAL = 102; // current goal
string TASKDONETEXT = "You've found the salt!"; // Mission complete message
string RUMOR_TASK_DONE = " found the red salt."; // rumor done
string TASKDONEUUID = "f78027c9-e8bb-38f2-9b11-1d4e89ac10a4"; // sound byte to play
string PRIZENAME = "NONE"; // prize to give when mission complete
// Next Task-specific info
integer ADVTASKTDNUM = 103; // next task to hand out when task is done
string ADVTASKTODO = "Return the salt to the baker"; // the next goal message
string ADVTASKTODOHINT = "He's where you first met him"; // hint for the next goal
float TOUCH_RANGE = 1.5; // in meters, how close to be for touch to work arms & legs = 1.5m
integer PRIZEWAIT = 3600; // seconds to remember players who got prize?
float EVENTTIMER = 15.0; // seconds between running memory and list cleanup timed events
//============================================================================
// MESSAGE FORMAT REFERENCE
//============================================================================
// Ask a player HUD if the player is in an adventure - InAdv?
// Player responds:
// Yes in an adventure - InAdv | String AdventureName
// No, not in adventure - InAdv | NONE
// Task In Progress Query - Ask player what their current goal is
// NPC/Object Send Example: TaskIP?
// Task In Progress Response - The player Responds with current task in progress
// Player Send Example: TaskIP | AdventureGoal
// Task Done - Tell player they have achieved their current goal
// NPC Object Send Example: DoneTask | GoalText | TaskDone Text | PlayerUUID
// Add a task to the Player HUD - AddTask | TaskNumber | String Describing Task
// Add a hint for a task to the Player HUD - AddHint | TaskNumber | String Hint
//============================================================================
// GLOBAL CONSTANTS
//============================================================================
string MSG_STARTUP = "Baroun's Adventure Machine is activating";
string CHAN_PREFIX = "0x"; // prefix for calculating dynamic channels from UUIDs
string API_DIVIDER = "|"; // The field divider within BAM packets
string API_INADV_QUERY = "InAdv?"; // ask a player HUD if the player is in an adventure?
string API_INADV_RESPONSE = "InAdv"; // player response to in adventure request
string API_TASKIP_QUERY = "TaskIP?"; // what is player task-in-progress?
string API_TASKIP_RESPONSE = "TaskIP"; // player says current task-in-progress
string API_DONETASK = "DoneTask"; // tell player the task is done
string API_NONE = "NONE"; // magic value for no current adventure, or no prize
string API_ADDTASK = "AddTask"; // add a task to player HUD
string API_ADDHINT = "AddHint"; // add a task hint to player HUD
//============================================================================
// GLOBAL RUNTIME
//============================================================================
list RECENT; // list people who recently checked this goal
list GOTPRIZES; // list of who got prizes [UUID,unixtime]
key AVKEY; // key of player
string AVNAME; // name of player
integer BAM; // Channel we listen on
integer TARGET; //channel of thing we're talking to
integer HANDLE; // hold a handle for the open listener
integer CHAN_REGION = -999;
integer HAND_RUMOR;
integer CHAN_RUMOR;
string API_RUMOR_FIND = "RUMOR_SERVER_FIND";
string API_RUMOR_FOUND = "RUMOR_SERVER_FOUND";
string API_RUMOR_PUT = "RUMOR_PUT";
integer FLAG_DEBUG;
//============================================================================
// DEBUG
//============================================================================
DEBUG(string debugmsg) {
if ( FLAG_DEBUG == TRUE ) llInstantMessage(llGetOwnerKey(llGetKey()),"DEBUG: "+debugmsg);
}
//============================================================================
// DEFAULT STATE
//============================================================================
default {
//------------------------------------------------------------------------
// LISTEN EVENT
//------------------------------------------------------------------------
listen(integer chan, string name, key id, string msg) {
chan = 0; // LSLINT
name = ""; // LSLINTlsl
// Rumor server found, save its channel number
if ( msg == API_RUMOR_FOUND ) {
CHAN_RUMOR = (integer)(CHAN_PREFIX + llGetSubString((string)id,0,6));
return;
}
// calculate the BAM dynamic channel of the person interacting with us
TARGET= (integer)(CHAN_PREFIX + llGetSubString((string)llGetOwnerKey(id),-7,-1));
list tokens = llParseString2List(msg, [API_DIVIDER], []); // split message apart around | symbols
string command = llList2String(tokens, 0); // assign first item in list as BAM command
string data = llList2String(tokens, 1); // assign second item in list as BAM data for command
// if they answer with the current adventure, then react accordingly
if ( command == API_INADV_RESPONSE ) { // player responded they are in an adventure
if ( data == ADVNAME ) { // are they in THIS adventure?
llSay(TARGET,API_TASKIP_QUERY); // if so, ask which task in this adventure they are working on
return; // exit early to save processing
}
return; // done processing "in adventure" responses, exit listen early
}
if ( command == API_TASKIP_RESPONSE ) { // player responded with their task in progress
if ( data == (string)ADVGOAL ) { // its the task for THIS object - player has found the goal
// tell player HUD the task is done! - DoneTask|(num)|(text)|UUID
llSay(TARGET, API_DONETASK + API_DIVIDER + (string)ADVGOAL+ API_DIVIDER +TASKDONETEXT+ API_DIVIDER+ TASKDONEUUID);
// now tell GM the task is done
DEBUG(llKey2Name(llGetOwnerKey(id))+" (adventure "+ADVNAME+"): finished task "+(string)ADVGOAL+" ("+TASKDONETEXT+").");
// Send a rumor that the player progressed in the quest
if ( CHAN_RUMOR != 0 ) {
string who = llKey2Name(llGetOwnerKey(id));
string rumor = API_RUMOR_PUT+API_DIVIDER+who+API_DIVIDER+who+RUMOR_TASK_DONE;
llRegionSay(CHAN_RUMOR,rumor);
}
// The Task is Done, Distribute the Prize, if any
if ( PRIZENAME != API_NONE && llListFindList(GOTPRIZES,[llGetOwnerKey(id)]) == -1 ) { // is there a prize at this step?
llGiveInventory(llGetOwnerKey(id),PRIZENAME); // give it over
GOTPRIZES = [ llGetOwnerKey(id), (llGetUnixTime() + PRIZEWAIT) ] + GOTPRIZES; // remember who and when
// tell the GM
DEBUG(llKey2Name(llGetOwnerKey(id))+" (adventure "+ADVNAME+") prize given: "+PRIZENAME);
}
// Does finishing this task trigger a new task? If so, add it and a hint to the HUD
if ( ADVTASKTDNUM != 0 ) { // there is a "TODO" task too
// assign the next task to the player HUD
llSay(TARGET, API_ADDTASK + API_DIVIDER +(string)ADVTASKTDNUM + API_DIVIDER + ADVTASKTODO);
// assign the next task HINT to the player HUD
llSay(TARGET, API_ADDHINT + API_DIVIDER +(string)ADVTASKTDNUM + API_DIVIDER + ADVTASKTODOHINT);
// tell the GM the player is on the next task
DEBUG(llKey2Name(llGetOwnerKey(id))+" (adventure "+ADVNAME+"): Assigning next task "+(string)ADVTASKTDNUM+" ("+ADVTASKTODO+")");
}
return; // we're done with API_TASKIP_RESPONSE, so exit listen early in case we add more commands later
} // if data = ADVGOAL
return; // return early since we are done with TASKIP responses, in case we add more later
} // end if command equal TASKIP reponse
}
//------------------------------------------------------------------------
// STATE_ENTRY EVENT
//------------------------------------------------------------------------
state_entry() {
FLAG_DEBUG = FALSE;
DEBUG(MSG_STARTUP);
// calculate our dynamic channel
BAM= (integer)(CHAN_PREFIX + llGetSubString((string)llGetKey(),-7,-1));
HANDLE = llListen(BAM,"",NULL_KEY,""); // start a listener with llremove handle
if ( HAND_RUMOR != 0 ) llListenRemove(HAND_RUMOR);
CHAN_RUMOR = (integer)(CHAN_PREFIX + llGetSubString((string)llGetKey(),0,6));
HAND_RUMOR = llListen(CHAN_RUMOR,"",NULL_KEY,"");
llRegionSay(CHAN_REGION,API_RUMOR_FIND); // send request for rumor server channel
llSetTimerEvent(EVENTTIMER); // set a timer to manage the recent list
}
//------------------------------------------------------------------------
// 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?
DEBUG("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
}
//------------------------------------------------------------------------
// TOUCH EVENT
//------------------------------------------------------------------------
touch_start(integer times_touched) {
while (times_touched--) { // count down through all touches in this event
AVKEY=llDetectedKey(times_touched); // get the UUID of the toucher
AVNAME=llDetectedName(times_touched); // get the name of the toucher
float dist = llVecDist(llGetPos(),llList2Vector(llGetObjectDetails(AVKEY,[OBJECT_POS]),0)); // find distance between item and toucher
if ( dist <= TOUCH_RANGE ) { // is toucher within arms reach?
if ( llListFindList(RECENT,[AVKEY]) == -1 ) { // is player in recent list?
RECENT = [AVKEY] + RECENT; // no, add player to list
}
// calculate player-specific BAM dynamic channel
TARGET= (integer)(CHAN_PREFIX + llGetSubString((string)AVKEY,-7,-1));
llSay(TARGET, API_INADV_QUERY); // ask player if they are in adventure, must be within 10m for effect
}
}
}
}
//============================================================================
// END
//============================================================================