// Myriad_Lite-v01.1.11-20120826.lsl
// Copyright (c) 2012 by 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/
// CONSTANTS - DO NOT CHANGE DURING RUN
string BASENAME = "Myriad Lite"; // base name of this script without version or date
string VERSION = "0.1.11"; // Allen Kerensky's script version
string VERSIONDATE = "20120826"; // Allen Kerensky's script yyyymmdd
string TRUENAME; // name to reset item to after talker/emoter use
integer MINSTAT = 1; // min value for statistics
integer MAXSTAT = 5; // max human value for a statistic/attribute
integer MINRESILIENCE = 1; // min value for resilience
integer MAXRESILIENCE = 20; // max value for resilience
integer MINSKILL = 1; // min value for skill rank
integer MAXSKILL = 5; // max value for skill rank
integer CHANMYRIAD = -999; // chat sent to ALL Myriad players in region
integer CHANCOMMAND = 5; // chat sent by player to their meter
string DIV = "|"; // message field divider
string CHAN_PREFIX = "0x"; // channel prefix for calculating dynamic channels
integer MAXARMOR = 5; // max legal armor rating
// Module to Module Messaging Constants
integer MODULE_HUD = -1;
//integer MODULE_CHARSHEET = -2;
//integer MODULE_ARMOR = -3;
//integer MODULE_BAM = -4;
//integer MODULE_RUMORS = -5;
//integer MODULE_CLOSE = -6;
//integer MODULE_RANGED = -7;
//integer MODULE_RESILIENCE = -8;
//integer MODULE_PROGRESS = -9;
//integer MODULE_WELL = -10;
//integer MODULE_METER = -11;
//integer MODULE_GOON = -12;
integer LM_SENDTOATTACHMENT = 0x80000000;
// RUNTIME GLOBALS - CAN CHANGE DURING RUN
integer FLAG_DEBUG; // see debug messages?
key PLAYERID = NULL_KEY; // cached player UUID
string PLAYERNAME = ""; // cached player name
string NAME = ""; // character name
string SPECIES = ""; // character species
string BACKGROUND = ""; // character childhood history
string CAREER = ""; // character career or faction
list STATISTICS = [];
list RESILIENCES = [];
list CURRENT_RESILIENCES = [];
list SKILLS = []; // skills [ string SkillName, integer SkillRank ]
integer HANDMYRIAD = 0; // Myriad channel handle
integer CHANPLAYER = 0; // dynamic channel to one player's UUID
integer HANDPLAYER = 0; // player channel handle
integer CHANOBJECT = 0; // dynamic channel to one object's UUID
integer CHANHUD = 0; // dyname channel to HUD object UUID
integer HANDHUD = 0; // HUD channel handle
integer HANDCOMMAND = 0; // command channel handle
integer HANDATTACH = 0; // attachment channel handle
integer CHANATTACH = 0; // dynamic channel for attachments
integer CHANBAM = 0; // dynamic channel for BAM quests
integer HANDBAM = 0; // BAM channel update
// Talker/Emoter
integer CHANNEL_OOCCHAT = 22;
integer CHANNEL_OOCEMOTE = 23;
integer CHANNEL_ICCHAT = 44;
integer CHANNEL_ICTHINK = 45;
integer CHANNEL_ICEMOTE = 66;
integer CHANNEL_NARRATE = 88;
integer HANDLE_OOCCHAT; // callback handle for llListen
integer HANDLE_OOCEMOTE; // callback handle for llListen
integer HANDLE_ICCHAT; // callback handle for llListen
integer HANDLE_ICTHINK; // callback handle for llListen
integer HANDLE_ICEMOTE; // callback handle for llListen
integer HANDLE_NARRATE; // callback handle for llListen
string OOCPREFIX="(("; // what to put before OOC messages
string OOCSUFFIX="))"; // what to put after OOC messages
integer FLAG_ANIMATE; //
integer FLAG_INCAPACITATED; // incapacitated by wounds?
integer FLAG_DEAD; // killed by critical wounds?
integer CURARMOR = 0; // highest armor value worn out of all armor worn, not a total
// ABILITY TEST
// Requires ATTRIBUTE NAME, SKILL NAME
// Returns the ability test score for use by success fail, opposed rolls, etc
// See Myriad PDF page 18, Myriad Special Edition page 24
integer ABILITY_TEST(integer attribute,integer skill) {
integer highroll = 0; // clear out the highest roll
while( attribute-- ) { // roll a dice for each point of the attribute
integer roll = 1+(integer)llFrand(5.0); // roll this d6
if ( roll > highroll) highroll = roll; // if this is highest roll so far, remember it
} // finished rolling a dice for each point of the base attribute
return highroll + skill; // now, return the total of highest dice roll + skill value
}
// CHECK AMMO
CHECKAMMO() {
llWhisper(CHANATTACH,"CHECKAMMO");
}
// COMBATOFF - turn off fist fighter
COMBATOFF() {
llMessageLinked(LINK_THIS,MODULE_HUD,"COMBATOFF",llGetOwner());
}
// COMBATON - turn on fist fighter
COMBATON() {
llMessageLinked(LINK_THIS,MODULE_HUD,"COMBATON",llGetOwner());
}
// COMMAND - process chat and link message commands together
COMMAND(string msg) {
// break down the commands and messages into units we can work with
list fields = llParseString2List(msg,[DIV],[]); // break into list of fields based on DIVider
string command = llToLower(llStringTrim(llList2String(fields,0),STRING_TRIM)); // assume the first field is a Myriad Lite command
if ( command == "checkammo" ) { CHECKAMMO(); return;} // check ammo in weapons
if ( command == "combatoff") { COMBATOFF(); return; } // turn off fist fighter
if ( command == "combaton" ) { COMBATON(); return; } // turn on the fist fighter
if ( command == "credits" ) { CREDITS(); return;} // show the credits including version number
if ( command == "debugoff" ) { DEBUGOFF(); return; } // player turn off debugging
if ( command == "debugon" ) { DEBUGON(); return;} // player turn on debugging
if ( command == "drawboth" ) { DRAW("both"); return; } // draw both weapons
if ( command == "drawleft" ) { DRAW("left"); return; } // draw weapon in left hand
if ( command == "drawright" ) { DRAW("right"); return; } // draw weapon using right hand
if ( command == "holsterboth" ) { HOLSTER("both"); return; } // holster both weapons
if ( command == "holsterleft" ) { HOLSTER("left"); return; } // holster weapon in left hand
if ( command == "holsterright" ) { HOLSTER("right"); return; } // holster weapon in right hand
if ( command == "quest" ) { QUEST(); return; } // check our current quest status
if ( command == "reload" ) { RELOAD(); return;} // reload weapons
if ( command == "reset" ) { RESET(); return;} // reset HUD
//if ( command == "rumor" ) { RUMOR(msg); return;} // rumors
if ( command == "safetyoff" ) { SAFETYOFF(); return;} // unsafe the weapons
if ( command == "safetyon" ) { SAFETYON(); return;} // safe the weapons
if ( command == "sheatheboth" ) { SHEATHE("both"); return; } // sheathe both weapons
if ( command == "sheatheleft" ) { SHEATHE("left"); return; } // sheathe weapon in left hand
if ( command == "sheatheright" ) { SHEATHE("right"); return; } // sheathe weapon in right hand
if ( command == "version" ) { CREDITS(); return;} // show the credits including version number
if ( command == "incapacitated" ) { FLAG_INCAPACITATED = TRUE; return; }
if ( command == "dead" ) { FLAG_DEAD = TRUE; return; }
if ( command == "alive" ) { FLAG_DEAD = FALSE; return; }
if ( command == "revived" ) { FLAG_INCAPACITATED = FALSE; return; }
if ( llGetSubString(llStringTrim(llToLower(command),STRING_TRIM),0,4) == "armor" ) { SENDTOMODULE(msg,PLAYERID); return; }
if ( llGetSubString(llStringTrim(llToLower(command),STRING_TRIM),0,4) == "rumor" ) { RUMOR(msg); return; }
if ( llGetSubString(llStringTrim(llToLower(command),STRING_TRIM),0,3) == "roll" ) { ROLL(msg); return; } // roll dice
}
// CREDITS comply with Myriad RPG Creative Common-Attribution legal requirement
CREDITS() {
llOwnerSay("The Myriad RPG System was designed, written, and illustrated by Ashok Desai.");
llOwnerSay("RPG System licensed under the Creative Commons Attribution 2.0 UK: England and Wales.");
llOwnerSay("Myriad Lite v"+VERSION+" "+VERSIONDATE+" Copyright (c) 2011 by Allen Kerensky (OSG/SL)");
llOwnerSay("Licensed under Creative Commons Attribution-Share Alike-Non-Commercial 3.0 Unported.");
llMessageLinked(LINK_THIS,MODULE_HUD,"VERSION",llGetOwner());
}
// DEBUG - show debug chat with wearer name for sorting
DEBUG(string dmessage) {
if ( FLAG_DEBUG == TRUE ) { // are we debugging?
llSay(DEBUG_CHANNEL,"("+llKey2Name(PLAYERID)+") HUD: "+dmessage);
}
}
// DEBUGOFF - turn off the DEBUG flag
DEBUGOFF() {
FLAG_DEBUG = FALSE; // set debug flag to FALSE
llOwnerSay("Debug Mode Deactivated");
llMessageLinked(LINK_THIS,MODULE_HUD,"DEBUGOFF",llGetOwner());
}
// DEBUGON - turn on the DEBUG flag
DEBUGON() {
FLAG_DEBUG = TRUE; // set debug flag TRUE
llOwnerSay("Debug Mode Activated");
llMessageLinked(LINK_THIS,MODULE_HUD,"DEBUGON",llGetOwner());
}
// DRAW weapons
DRAW(string hand) {
if ( hand == "left" ) { llWhisper(CHANATTACH,"DRAWLEFT"); return; } // draw left-hand weapon
if ( hand == "right" ) { llWhisper(CHANATTACH,"DRAWRIGHT"); return; } // draw right-hand weapon
if ( hand == "both" ) { llWhisper(CHANATTACH,"DRAWBOTH"); return; } // draw both weapons
}
// ERROR - show errors on debug channel with wearer name for sorting
ERROR(string emessage) {
llSay(DEBUG_CHANNEL,"ERROR ("+llKey2Name(PLAYERID)+"): "+emessage);
}
// HOLSTER weapons
HOLSTER(string hand) {
if ( hand == "left" ) { llWhisper(CHANATTACH,"HOLSTERLEFT"); return; } // holster left-hand weapon
if ( hand == "right" ) { llWhisper(CHANATTACH,"HOLSTERRIGHT"); return; } // holster right-hand weapon
if ( hand == "both" ) { llWhisper(CHANATTACH,"HOLSTERBOTH"); return; } // holster both weapons
}
// METER - update a hovertext health meter or HUD bar graph
METER() {
llMessageLinked(LINK_THIS,MODULE_HUD,"METER",PLAYERID);
}
// QUEST STATUS
QUEST() {
llMessageLinked(LINK_THIS,MODULE_HUD,"BAMSTATUS",PLAYERID); // send a status request to BAM Modules
}
// RELOAD
RELOAD() {
llWhisper(CHANATTACH,"RELOAD");
}
// RESET - shut down running animations then reset the script to reload character sheet
RESET() {
if ( FLAG_DEAD == TRUE || FLAG_INCAPACITATED == TRUE ) { // don't allow reset if already on respawn timer
llOwnerSay("Cannot reset while incapacitated or dead. You will respawn in a few moments.");
return;
}
llOwnerSay("Resetting Myriad Lite. Please wait...");
// stop all running animations
if ( FLAG_ANIMATE == TRUE ) { // do we have permission to animate?
list anims = llGetAnimationList(PLAYERID); // get list of current animations for owner
integer animcount = llGetListLength(anims); // count the number of animations in the list
while (animcount--) { // step from end of animation list to beginning
llStopAnimation(llList2String(anims,animcount)); // stopping each animation
}
}
llMessageLinked(LINK_THIS,MODULE_HUD,"RESET",PLAYERID); // send reset to modules
llResetScript(); // now reset this script
}
// ROLL DICE
ROLL(string message) {
list input = llParseString2List(message, [" ","d"],[] );
string command = llList2String(input,0);
integer numberOfDice = llList2Integer(input,1);
integer numberOfSides = llList2Integer(input,2);
string output = "";
integer total = 0;
if ( command == "roll" ) {
if ( numberOfDice >= 1 && numberOfDice <= 20 ) {
if ( numberOfSides > 1 && numberOfSides <= 100 ) {
integer index;
for( index = 1; index <= numberOfDice; index++) {
integer roll = (integer)((llFrand(1.0) * numberOfSides)+1);
output += (string)roll;
output += ", ";
total += roll;
}
string objname = llGetObjectName();
llSetObjectName("(("+llKey2Name(llGetOwner()));
llWhisper(PUBLIC_CHANNEL,"/me rolls "+(string)numberOfDice+"d"+(string)numberOfSides+" resulting in "+output+" totalling "+(string)total+".))");
llSetObjectName(objname);
return;
}
}
llOwnerSay("To roll, say /5 roll #d# or /5 roll #d#. For example, saying /5 roll 1d20 in chat rolls one 20-sided dice.\"");
}
}
// RPEVENT
RPEVENT(string rpevent) {
string oldname = llGetObjectName(); // save the current object name
llSetObjectName("Myriad RP Event"); // change the object name to
llOwnerSay(rpevent); // now tell the owner the rest of the RPEVENT| message
//llRegionSay(CHANMYRIAD,"RPEVENT|"+NAME+" ("+PLAYERNAME+") "+rpevent);
llSetObjectName(oldname); // restore the HUD back to its original name
}
// RUMOR CONTROL
RUMOR(string cmdrumor) {
DEBUG("Sending to rumor module: "+cmdrumor);
llMessageLinked(LINK_THIS,MODULE_HUD,cmdrumor,PLAYERID); // relay rumor commands to module
}
// SAFETY OFF
SAFETYOFF() {
llWhisper(CHANATTACH, "SAFETYOFF");
}
// SAFETY ON
SAFETYON() {
llWhisper(CHANATTACH, "SAFETYON");
}
// SENDTOATTACHMENT
SENDTOATTACHMENT(string msg) {
DEBUG("SENDTOATTACHMENT("+msg+")");
llWhisper(CHANATTACH,msg);
}
// SENDTOMODULE
SENDTOMODULE(string msg,key speaker) {
DEBUG("SENDTOMODULE("+msg+")");
llMessageLinked(LINK_THIS,MODULE_HUD,msg,speaker);
}
// SETUP - begin bringing the HUD online
SETUP() {
FLAG_DEBUG = FALSE;
CREDITS(); // show Myriad credits as required by the Creative Commons - Attribution license
PLAYERID = llGetOwner(); // remember the owner's UUID
PLAYERNAME = llKey2Name(PLAYERID); // remember the owner's legacy name
llSetText("",<0,0,0>,0); // clear any previous hovertext
// Talker/Emoter setup
TRUENAME = BASENAME + " " + VERSION + " " + VERSIONDATE; // put together full item name for talker/emoter
llSetObjectName(TRUENAME); // force object title back if the talker/emoter messed it up
//llOwnerSay("Character Sheet loaded. You are now ready to roleplay.");
if ( HANDMYRIAD != 0 ) llListenRemove(HANDMYRIAD);
HANDMYRIAD = llListen(CHANMYRIAD,"",NULL_KEY,""); // setup listener for Myriad RP events
if ( HANDCOMMAND != 0 ) llListenRemove(HANDCOMMAND);
HANDCOMMAND = llListen(CHANCOMMAND,"",PLAYERID,""); // listen to chat commands from owner
CHANHUD = (integer)("0x"+llGetSubString((string)llGetKey(),0,6)); // calculate a player-specfic dynamic chat channel
if ( HANDHUD != 0 ) llListenRemove(HANDHUD);
HANDHUD = llListen(CHANHUD,"",NULL_KEY,""); // listen on the HUD object dynamic chat channel
CHANPLAYER = (integer)("0x"+llGetSubString((string)PLAYERID,0,6)); // calculate a player-specfic dynamic chat channel
if ( HANDPLAYER != 0 ) llListenRemove(HANDPLAYER);
HANDPLAYER = llListen(CHANPLAYER,"",NULL_KEY,""); // listen on the player dynamic chat channel
CHANATTACH = (integer)("0x"+llGetSubString((string)PLAYERID,1,7)); // attachment-specific channel
if ( HANDATTACH != 0 ) llListenRemove(HANDATTACH);
HANDATTACH = llListen(CHANATTACH,"",NULL_KEY,""); // listen for messages from attachments
CHANBAM = (integer)(CHAN_PREFIX + llGetSubString((string)PLAYERID,-7,-1));
if ( HANDBAM != 0 ) llListenRemove(HANDBAM);
HANDBAM = llListen(CHANBAM,"",NULL_KEY,""); // start listener with listenremove handle
// Talker/Emoter
if ( HANDLE_OOCCHAT != 0 ) llListenRemove(HANDLE_OOCCHAT);
HANDLE_OOCCHAT = llListen(CHANNEL_OOCCHAT,"",PLAYERID,"");
if ( HANDLE_OOCEMOTE != 0 ) llListenRemove(HANDLE_OOCEMOTE);
HANDLE_OOCEMOTE = llListen(CHANNEL_OOCEMOTE,"",PLAYERID,"");
if ( HANDLE_ICCHAT != 0 ) llListenRemove(HANDLE_ICCHAT);
HANDLE_ICCHAT = llListen(CHANNEL_ICCHAT,"",PLAYERID,"");
if ( HANDLE_ICTHINK != 0 ) llListenRemove(HANDLE_ICTHINK);
HANDLE_ICTHINK = llListen(CHANNEL_ICTHINK,"",PLAYERID,"");
if ( HANDLE_ICEMOTE != 0 ) llListenRemove(HANDLE_ICEMOTE);
HANDLE_ICEMOTE = llListen(CHANNEL_ICEMOTE,"",PLAYERID,"");
if ( HANDLE_NARRATE != 0 ) llListenRemove(HANDLE_NARRATE);
HANDLE_NARRATE = llListen(CHANNEL_NARRATE,"",PLAYERID,"");
llOwnerSay("Registering any Myriad Lite-compatible attachments...");
llWhisper(CHANATTACH,"REGISTERATTACHMENTS"); // ask for attachments on their dynamic channel
llRequestPermissions(PLAYERID,PERMISSION_TRIGGER_ANIMATION); // request perms to run stop all animation code if needed
QUEST(); // update the BAM Module
llOwnerSay("Myriad Lite HUD startup complete.");
}
// SHEATHE weapons
SHEATHE(string hand) {
if ( hand == "left" ) { llWhisper(CHANATTACH,"SHEATHELEFT"); return; } // sheathe left-hand weapon
if ( hand == "right" ) { llWhisper(CHANATTACH,"SHEATHERIGHT"); return; } // sheathe right-hand weapon
if ( hand == "both" ) { llWhisper(CHANATTACH,"SHEATHEBOTH"); return; } // sheathe both weapons
}
// An Unopposed Ability Test - Myriad PDF p. 19, Myriad Special Edition p. 25
// Requires TargetNumber, Attribute Name, Skill Name
// Returns TRUE for Success and False for Fail
integer UNOPPOSED_TEST(integer targetnum,integer tattribute,integer tskill ) {
integer check = ABILITY_TEST(tattribute,tskill); // calculate the player's ability test value
if ( check >= targetnum ) return TRUE; // player won the ability test
return FALSE; // player lost the ability test
}
// DEFAULT STATE - load character sheet
default {
// ATTACH - logged in with meter or worn from inventory/ground while running
attach(key id) {
if ( id != NULL_KEY ) {
RESET();
}
}
// CHANGED - triggered for many changes to the avatar
// TODO reload sim-specific settings on region change
changed(integer changes) {
if ( changes & CHANGED_INVENTORY ) { // inventory changed somehow?
llOwnerSay("Inventory changed. Reloading character sheet.");
RESET(); // saved a new character sheet? - reset and re-read it.
}
if ( changes & CHANGED_REGION || changes & CHANGED_TELEPORT ) {
llRequestPermissions(PLAYERID,PERMISSION_TRIGGER_ANIMATION);
METER(); // update the meter after a shift
}
}
// LINK MESSAGE - commands to and from other prims in HUD
link_message(integer sender,integer sending_module,string str, key id) {
if ( sending_module == MODULE_HUD ) return; // ignore our own link messages
DEBUG("EVENT: link_message("+(string)sender+","+(string)sending_module+","+str+","+(string)id+")");
list fields = llParseString2List(str,[DIV],[]); // break into list of fields based on DIVider
string command = llToLower(llStringTrim(llList2String(fields,0),STRING_TRIM)); // assume the first field is a Myriad Lite command
string data = llStringTrim(llList2String(fields,1),STRING_TRIM); // field one is the data
list subfields = llParseString2List(data,["="],[]); // break data field into comma-delimited subfields if needed
if ( command == "set_name" ) {
NAME = llList2String(subfields,1); // set the name
return;
}
if ( command == "set_species" ) {
SPECIES = llList2String(subfields,1); // set the species;
return;
}
if ( command == "set_background" ) {
BACKGROUND = llList2String(subfields,1); // set the species;
return;
}
if ( command == "set_career" ) {
CAREER = llList2String(subfields,1); // set the species;
return;
}
if ( command == "set_statistic" ) {
string statname = llList2String(subfields,0); // find the boon name
integer statrank = llList2Integer(subfields,1); // find the boon rank value
// TODO how to verify stat names are valid?
if ( statrank >= MINSTAT && statrank <= MAXSTAT ) { // rank valid?
STATISTICS = [statname,statrank] + STATISTICS; // add statistic to list
} else { // invalid, report it
ERROR("STATISTIC "+statname+" rank "+(string)statrank+" value out of allowed range: "+(string)MINSTAT+"-"+(string)MAXSTAT);
}
return;
}
if ( command == "set_resilience" ) {
string resname = llList2String(subfields,0); // find the boon name
integer resrank = llList2Integer(subfields,1); // find the boon rank value
// TODO how to verify resilience names are valid?
if ( resrank >= MINRESILIENCE && resrank <= MAXRESILIENCE ) { // rank valid?
RESILIENCES = [resname,resrank] + RESILIENCES; // add resilience to list
CURRENT_RESILIENCES = [resname,resrank] + CURRENT_RESILIENCES; // add to current list too
} else { // invalid, report it
ERROR("RESILIENCE "+resname+" rank "+(string)resrank+" value out of allowed range: "+(string)MINRESILIENCE+"-"+(string)MAXRESILIENCE);
}
return;
}
if ( command == "set_skill" ) {
string skillname = llList2String(subfields,0); // find the skill name
integer skillrank = llList2Integer(subfields,1); // find the skill rank
// TODO how to verify skill names are valid?
if ( skillrank >= MINSKILL && skillrank <= MAXSKILL ) { // skill rank valid?
SKILLS = [skillname,skillrank] + SKILLS; // add skill to list
} else { // invalid, report it
ERROR("SKILL "+skillname+" rank "+(string)skillrank+" value out of allowed range: "+(string)MINSKILL+"-"+(string)MAXSKILL);
}
return;
}
if ( command == "character_loaded" ) {
METER();
return; // we're out of notecard, so character sheet is loaded - start playing
}
if ( command == "armorcurrent" ) { // ARMORCURRENT|integer newcurrentarmor
integer rating = llList2Integer(fields,1);
if ( rating >= 0 && rating <= MAXARMOR ) {
CURARMOR = rating;
}
return;
}
if ( llGetSubString(command,0,4) == "armor" ) { SENDTOATTACHMENT(str); return; } // process armor messages
if ( sending_module == LM_SENDTOATTACHMENT ) { SENDTOATTACHMENT(str); return; } // send module messages to attachments
if ( command == "rpevent" ) { RPEVENT(llList2String(fields,1)); return; }
COMMAND(str); // send to shared command processor for chat and link messages
return;
}
// LISTEN - the main Myriad Lite message processor for RP events and player commands
listen(integer channel, string speakername, key speakerid, string message) {
DEBUG("HUD Listen: channel=["+(string)channel+"] name=["+speakername+"] id=["+(string)speakerid+"] message=["+message+"]");
speakername = ""; // LSLINT
// calculate the dynamic channel of who is speaking in case we need to return commands
CHANOBJECT = (integer)(CHAN_PREFIX+llGetSubString((string)speakerid,0,6));
// break down the commands and messages into units we can work with
list fields = llParseString2List(message,[DIV],[]); // break into list of fields based on DIVider
string command = llList2String(fields,0); // assume the first field is a Myriad Lite command
// --- PLAYER COMMAND CHANNEL
if ( channel == CHANCOMMAND ) { // handle player chat commands
COMMAND(message); // send to shared command processor for chat and link messages
return;
} // end of if channel == player commands
// --- BAM CHANNEL
if ( channel == CHANBAM ) {
SENDTOMODULE(message,speakerid); // send BAM to Module
return;
} // end if channel BAMCHAN
// --- Myriad Lite regionwide messages
if ( channel == CHANMYRIAD ) { // handle Myriad system messages
if ( command == "RPEVENT" ) { // Myriad Lite RPEVENT - roleplay events everyone might find interesting
RPEVENT(llList2String(fields,1));
return;
} // end if RPEVENT
return;
} // end if channel == CHANMYRIAD
// --- ATTACHMENT CHANNEL
if ( channel == CHANATTACH ) { // handle the attachment commands
if ( FLAG_DEAD == TRUE || FLAG_INCAPACITATED == TRUE ) return; // can't mess with attachments while down
if ( llToLower(llGetSubString(llStringTrim(command,STRING_TRIM),0,4)) == "armor" ) { SENDTOMODULE(message,PLAYERID); return; } // process armor messages
if ( command == "ATTACHMELEE" || command == "ATTACHRANGED" ) { // holding a weapon rather than using fists?
llMessageLinked(LINK_THIS,MODULE_HUD,message,PLAYERID);
return;
}
if ( command == "DETACHMELEE" || command == "DETACHRANGED" ) { // are we going back to fists?
llMessageLinked(LINK_THIS,MODULE_HUD,message,PLAYERID);
return;
}
if ( command == "ATTACHMETER" || command == "DETACHMETER" ) {
llMessageLinked(LINK_THIS,MODULE_HUD,message,PLAYERID);
//METERWORN = TRUE; // we need to send meter events
//METER(); // send update
return;
}
}
// --- CHANHUD - get region settings messages
if ( channel == CHANHUD ) {
llMessageLinked(LINK_THIS,MODULE_HUD,message,speakerid);
return; // done with the HUD dynamic channel message processing, return
}
// --- CHANPLAYER
if ( channel == CHANPLAYER ) { // handle player dynamic commands
if ( command == "RPEVENT" ) { // Myriad Lite RPEVENT - roleplay events everyone might find interesting
string oldname = llGetObjectName(); // save the current object name
llSetObjectName("Myriad RP Event (Private)"); // change the object name to
llOwnerSay(llList2String(fields,1)); // now tell the owner the rest of the RPEVENT| message
llSetObjectName(oldname); // restore the HUD back to its original name
return;
} // end if RPEVENT
// incoming message from rumor server?
if ( llGetSubString(llToLower(llStringTrim(command,STRING_TRIM)),0,4) == "rumor" ) {
llMessageLinked(LINK_THIS,MODULE_HUD,message,speakerid); // send message and key of speaker to rumors
return;
}
if ( command == "UNOPPOSED_CHECK" ) { // object in sim wants a simple skill check
integer targetnum = llList2Integer(fields,1); // what is unopposed check target num?
integer tattrib = llList2Integer(fields,2); // target attribute
integer tskill = llList2Integer(fields,3); // target skill
UNOPPOSED_TEST(targetnum,tattrib,tskill);
return;
}
if ( (command == "CLOSECOMBAT" || command == "RANGEDCOMBAT") && (FLAG_DEAD == TRUE || FLAG_INCAPACITATED == TRUE ) ) return; // can't fight when dead
// The last action of the PLAYER CHAN processing is to put the message on the link-message bus
// the speakerid is sent as the uuid field in case the modules need to send a message back out to another dynamic channel object
// Incoming messages that go onto the bus to get handled by modules include:
// CLOSEHIT - we've been hit and have to make an opposed ability test to avoid it
// RANGEDHIT - mortal combat ranged combat attack message
// HEALPARTIAL - heal some number of boxes of resilience
// HEALFULL - heal 100 boxes of resilience
// DUMP_PROGRESS - request from character builder to dump progress since last character build/progress session
// GET_XP
// SET_XP
// GET_XPLEVEL
// SET_XPLEVEL
// GET_STAT
// GET_RESILIENCE
// GET_MAXRESILIENCE
// SET_RESILIENCE
llMessageLinked(LINK_THIS,MODULE_HUD,message,speakerid);
return; // done with the player channel message processing, return
} // end of if channel CHANPLAYER
// Handle Out-Of-Character speaking
if ( channel == CHANNEL_OOCCHAT ) {
llSetObjectName(OOCPREFIX+PLAYERNAME);
llSay(PUBLIC_CHANNEL,"/me says, \""+message+"\""+OOCSUFFIX);
llSetObjectName(TRUENAME);
return;
}
// Handle Out-Of-Character emotes
if ( channel == CHANNEL_OOCEMOTE ) {
llSetObjectName(OOCPREFIX+PLAYERNAME);
llSay(PUBLIC_CHANNEL,"/me "+message+OOCSUFFIX);
llSetObjectName(TRUENAME);
return;
}
// Handle In-Character speaking
if ( channel == CHANNEL_ICCHAT ) {
llSetObjectName(NAME);
llSay(PUBLIC_CHANNEL,"/me says, \""+message+"\"");
llSetObjectName(TRUENAME);
return;
}
// Handle In-Character thinking
if ( channel == CHANNEL_ICTHINK ) {
llSetObjectName(NAME);
llSay(PUBLIC_CHANNEL,"/me thinks, \'"+message+"\'");
llSetObjectName(TRUENAME);
return;
}
// Handle In-Character emotes
if ( channel == CHANNEL_ICEMOTE ) {
llSetObjectName(NAME);
llSay(PUBLIC_CHANNEL,"/me "+message);
llSetObjectName(TRUENAME);
return;
}
// Handle Narration
if ( channel == CHANNEL_NARRATE ) {
llSetObjectName("");
llSay(PUBLIC_CHANNEL,"/me "+message);
llSetObjectName(TRUENAME);
return;
}
} // end listen
// ON_REZ - logged in with meter, or worn from inventory while running
on_rez(integer param) {
param = 0; // LSLINT
RESET(); // a reset to reload character
}
// STATE ENTRY
state_entry() {
SETUP();
}
// TOUCH_START - touch HUD for adventure update
touch_start(integer total_number) {
total_number = 0; // LSLINT
string action = llGetLinkName(llDetectedLinkNumber(0)); // get name of prim clicked in link set
if ( action != "" && action != llGetObjectName() ) { // someone clicked a named button prim on this linkset
COMMAND(action); // try that prim name as a command
return;
}
METER();
QUEST();
}
} // end state running
// END