User:Allen Kerensky/Myriad Lite Preview 5/BAM Collision Goal

From OpenSimulator

< User:Allen Kerensky | Myriad Lite Preview 5
Revision as of 15:41, 6 February 2012 by Allen Kerensky (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Contents

Myriad Lite BAM Collision Goal

Each step/task of the quest or adventure is represented by a goal object.

There are two basic parts to configuring a Goal object:

  1. Configure the current task that the player must be working on, in order to have "successfully" found this goal.
  2. Configure a NEXT task to hand out when the player finishes this current task, to send the player on to the next goal.

There are currently 3 starter goal objects, with many more possible, to help nudge players through your quest.

Collision Goal

There is a collision goal, which the player must physically collide with to activate.

This is useful as a transparent/phantom "detector" that a player has entered a general area, to hand them the next task that is more specifically what to do in that area.

By combining these three goals, and clever task descriptions and hints, you can build an infinite number of scavenger-hunt style quests for players in your regions.

Setup

Configuring all three goals is a similar process:

  1. Create the goal object - such as a door, box, control panel button, etc.
  2. Drop in the goal script
  3. Edit the goal script ADVENTURE-SPECIFIC CONFIGURATION settings at the top.
    1. MSG_SETTEXT: the hovertext message over the quest goal. Example: "Processed Red Salt Cask";
    2. MSG_SETTEXT_COLOR: color of the hovertext. Examples: http://wiki.secondlife.com/wiki/Category:LSL_Color
    3. MSG_SETTEXT_ALPHA: alpha/transparency of the hovertext. Examples: 0.0 = invisible, 1.0 = fully visible.
    4. ADVNAME: short name of this adventure: Example "Red Salt Quest"
    5. ADVGOAL: the current task number that finding this goal will complete. Example: 101 Note: You can have multiple goal objects with the same goal number. That gives players multiple avenues to complete the same step of the quest.
    6. TASKDONETEXT: message to player they they found the goal to complete this task. Example: "You've found the salt mine! You'll need a lantern."
    7. TASKDONEUUID: uuid of a sound to play for player when they complete this step. Example: "step101done" Note: if you put a sound file in the goal object inventory, then this variable can be the name of the sound file.
    8. PRIZENAME: A prize to give player for completing this task step in the overrall quest. Example: "Cave Lantern" Note: Not every goal needs to give out a prize, but this lets you equip player with necessary bits to complete later tasks in the quest.
    9. ADVTASKTDNUM: the NEXT task number "to-do" now that player has completed THIS task. Example: 102 NOTE: You can have multiple goals with the same task number that give the same OR DIFFERENT Next Task numbers. This can channel players into different parts of your quests based on which goal items they activated at that step.
    10. ADVTASKTODO: a description of the NEXT task in the quest. Example: "Explore the mine for processed red salt."
    11. ADVTASKTODOHINT: a hint to help player complete the next task. Example: "Processed red salt is usually stored and shipped in casks or barrels"
    12. TRIGGERWAIT: how long must player wait before being able to re-trigger this goal, in seconds. Example: 60
    13. PRIZEWAIT: how long must player wait between getting the prize from this goal again, in seconds: Example 3600 (=1 hour, 86400 = 1 day)
    14. EVENTTIMER: time between processing the recent and prizegiven lists, in seconds. Example 15.0 Note: Smaller numbers mean more accuracy but more lag. Larger numbers mean less lag but less precise list processing and cleanup.
  4. Save and close the script.
  5. Place the next goal using steps 1-4 above...
  6. Set the last goal in the quest to give the player a TASK and HINT to go back to the quest NPC for the main quest prize.

BAM_Collision_Goal-v0.0.7-20120130.lsl

// Baroun's Adventure Machine BAM_Collision_Goal-v0.0.7-20120130.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 tasks don't overlap between adventures
//============================================================================

// Object or NPC-specific info
string MSG_SETTEXT = "Processed Red Salt Cask";
vector MSG_SETTEXT_COLOR = <0,0,1>;
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 salt!"; // Mission complete message
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

//============================================================================
// DEFAULT STATE
//============================================================================
default {
    //------------------------------------------------------------------------
    // STATE_ENTRY EVENT
    //------------------------------------------------------------------------
    state_entry() {
        llOwnerSay(MSG_STARTUP);
        llSetText(MSG_SETTEXT,MSG_SETTEXT_COLOR,MSG_SETTEXT_ALPHA);
        // calculate the BAM dynamic channel
        // FIXME - move listen open down into collision?
        BAM = (integer)(CHAN_PREFIX + llGetSubString((string)llGetKey(),-7,-1));
        HANDLE = llListen(BAM,"",NULL_KEY,""); // open a listener with handle
        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?
            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        
    }

    //------------------------------------------------------------------------
    // 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
        // 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
                llInstantMessage(llGetOwnerKey(llGetKey()),llKey2Name(llGetOwnerKey(id))+" (adventure "+ADVNAME+"): finished task "+(string)ADVGOAL+" ("+TASKDONETEXT+").");

                // 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
                    llInstantMessage(llGetOwnerKey(llGetKey()),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
                    llInstantMessage(llGetOwnerKey(llGetKey()),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
    }
}
//============================================================================
// END
//============================================================================

General
About This Wiki