User:Allen Kerensky/Myriad Lite/BAM Collision Goal-Preview6.lsl

From OpenSimulator

Jump to: navigation, search

BAM_Collision_Goal-Preview6.lsl

// Baroun's Adventure Machine BAM_Collision_Goal-Preview6.lsl
// Copyright (c) 2012 by Baroun Tardis and Allen Kerensky (OSG/SL) All Rights Reserved.
// This work is dual-licensed under
// Creative Commons Attribution (CC BY) 3.0 Unported
// http://creativecommons.org/licenses/by/3.0/
// - or -
// Modified BSD License (3-clause)
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright notice, 
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// * Neither the name of Myriad Lite nor the names of its contributors may be
//   used to endorse or promote products derived from this software without
//   specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
// NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The Myriad RPG System was designed, written, and illustrated by Ashok Desai
// Myriad RPG System licensed under:
// Creative Commons Attribution (CC BY) 2.0 UK: England and Wales
// http://creativecommons.org/licenses/by/2.0/uk/
 
string  VERSION = "0.0.8"; // version number
string  VERDATE = "20120704"; // version date
 
//============================================================================
// Adventure-Specific Configuration
// Task numbers are (AdvNum*100)+task, so tasks don't overlap between adventures
//============================================================================
 
// Object or NPC-specific info
string MSG_SETTEXT = "Processed Red Salt Cask";
vector MSG_SETTEXT_COLOR = <1,0,0>;
float  MSG_SETTEXT_ALPHA = 1.0;
 
// Adventure-specific info
string  ADVNAME="Red Salt"; // Adventure Name
 
// Current Task-specific info
integer ADVGOAL=102; // which task number is this a goal for?
string  TASKDONETEXT="You've found the red salt!"; // Mission complete message
string  RUMOR_TASK_DONE=" found some red salt."; // sent as rumor suffix
string  TASKDONEUUID="f78027c9-e8bb-38f2-9b11-1d4e89ac10a4"; // sound byte to play
string  PRIZENAME="NONE"; // prize to give when this task is complete
 
// Next Task-specific info
integer ADVTASKTDNUM=103; // next task to hand out when task is done
string  ADTASKTODO="Return the salt to the baker."; // the next task message
string  ADTASKTODOHINT="He's where you first met him."; // hint for the task goal
 
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
 
//============================================================================
// 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 AND API CONSTANTS
//============================================================================
string MSG_STARTUP          = "Baroun's Adventure Machine is activating";
string CHAN_PREFIX          = "0x"; // channel prefix for calculating dynamic channel numbers
string API_DIVIDER          = "|"; // The field divider within BAM messages
string API_INADV_QUERY      = "InAdv?"; // ask player HUD if player is in an adventure?
string API_INADV_RESPONSE   = "InAdv"; // player responds with adventure name they are in
string API_NONE             = "NONE"; // constant for player not in adventure
string API_TASKIP_QUERY     = "TaskIP?"; // what is the player's current task goal?
string API_TASKIP_RESPONSE  = "TaskIP"; // player response with current Task In Progress
string API_DONETASK         = "DoneTask"; // tell player they have achieved current goal
string API_ADDTASK          = "AddTask"; // add next task to player HUD
string API_ADDHINT          = "AddHint"; // add next task hint to the player HUD
 
//============================================================================
// GLOBAL RUNTIME VARIABLES
//============================================================================
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 avatar we're interacting with
string  AVNAME; // name of avatar we're interacting with.
integer BAM;    // Channel we listen on
integer TARGET; // channel of person or thing we're talking to
integer HANDLE; // integer handle of open listen requests
 
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 {
    //------------------------------------------------------------------------
    // COLLISION_START EVENT
    //------------------------------------------------------------------------
    collision_start(integer times_touched) {
        while (times_touched--) {
            AVKEY=llDetectedKey(times_touched); // UUID of who collided
            AVNAME=llDetectedName(times_touched); // name of who collided
            if ( llListFindList(RECENT,[AVKEY]) == -1 ) { // check recent list to see if collider is in it
                RECENT = [AVKEY,(llGetUnixTime()+TRIGGERWAIT)] + RECENT; // nope, so add new key to recent list
                // calculate BAMCHAN for avatar that hit collision goal
                TARGET= (integer)(CHAN_PREFIX + llGetSubString((string)AVKEY,-7,-1));
                llSay(TARGET, API_INADV_QUERY); // ask player if they are in a BAM adventure already
            }
        }
    }
 
    //------------------------------------------------------------------------
    // LISTEN EVENT
    //------------------------------------------------------------------------
    listen(integer chan, string name, key id, string msg) {
        chan = 0; // LSLINT
        name = ""; // LSLINT
 
        // 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 + ADTASKTODO);
                    // assign the next task HINT to the player HUD
                    llSay(TARGET, API_ADDHINT + API_DIVIDER +(string)ADVTASKTDNUM + API_DIVIDER + ADTASKTODOHINT);
                    // tell the GM the player is on the next task
                    DEBUG(llKey2Name(llGetOwnerKey(id))+" (adventure "+ADVNAME+"): Assigning next task "+(string)ADVTASKTDNUM+" ("+ADTASKTODO+")");
                }
                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);
        llSetText(MSG_SETTEXT,MSG_SETTEXT_COLOR,MSG_SETTEXT_ALPHA);
        // calculate the BAM dynamic channel
        // FIXME - move listen open down into collision?
        if ( HANDLE != 0 ) llListenRemove(HANDLE);
        BAM = (integer)(CHAN_PREFIX + llGetSubString((string)llGetKey(),-7,-1));
        HANDLE = llListen(BAM,"",NULL_KEY,""); // open a listener with 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); // fire off a timer event once an hour
    }
 
    //------------------------------------------------------------------------
    // 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        
    }
}
//============================================================================
// END
//============================================================================
Personal tools
General
About This Wiki