====== Myriad Lite Module Meter ====== // Myriad_Lite_Module_Meter-v0.0.4-20131026.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/ // FIXME PPMA // FIXME PPMA INCAP AND DEAD // FIXME more RPEVENTS // FIXME more HELP // FIXME CHECK FOR REQUIRED RECORDS IN PPMA string BASENAME = "Myriad Lite Module Meter"; string VERSION = "0.0.4"; // version number string VERSIONDATE = "20131026"; // version date // Module to Module Messaging Constants integer MODULE_METER = -11; integer LM_SENDTOATTACHMENT = 0x80000000; integer RENDEZVOUS1 = -999; // RUNTIME GLOBALS - CAN CHANGE DURING RUN integer CHANATTACH; // dynamic channel for attachments integer METERWORN; // using meter? // // PPMA GET_VAL // string KEY_NOT_FOUND = "[KEY_NOT_FOUND]"; string GET_VAL(string dbkey,string field) { //OWNERSAY("GET_VAL KEY=["+dbkey+"] FIELD=["+field+"]"); string out = KEY_NOT_FOUND; integer i; string name; string desc; for ( i = 2; i <= llGetNumberOfPrims(); i++ ) { name = llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0); desc = llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0); if ( llToLower(name) == llToLower(dbkey) && llToLower(desc) == llToLower(field) ) { out = llList2String(llGetLinkPrimitiveParams(i,[PRIM_TEXT]),0); //DEBUG("GET_VAL RETURN=["+out+"]"); return out; // bail on first match? } } //OWNERSAY("GET_VAL RETURN=["+out+"]"); return out; } //SET_VAL(string dbkey, string field, string val) { // //OWNERSAY("SET_VAL KEY=["+dbkey+"] FIELD=["+field+"] VAL=["+val+"]"); // integer i; // string name; // string desc; // integer written = FALSE; // for ( i = 2; i <= llGetNumberOfPrims(); i++ ) { // name = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0),STRING_TRIM); // desc = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0),STRING_TRIM); // if ( ( llToLower(name) == llToLower(dbkey) && llToLower(desc) == llToLower(field) ) && written == FALSE ) { // //OWNERSAY("SET_VAL UPDATE RECORD=["+(string)i+"] DBKEY=["+dbkey+"] DESC=["+field+"] VAL=["+val+"]"); // llSetLinkPrimitiveParamsFast(i,[PRIM_NAME,dbkey,PRIM_DESC,field,PRIM_TEXT,val,ZERO_VECTOR,0.0]); // written = TRUE; // we did an update, remember it // } // } // if ( written == TRUE ) { // //OWNERSAY("SET_VAL UPDATE COMPLETE."); // return; // } // // data hasn't been written, scan for free prim // for ( i = 2; i <= llGetNumberOfPrims(); i++ ) { // name = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0),STRING_TRIM); // desc = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0),STRING_TRIM); // if ( ( name == "" && desc == "" ) && written == FALSE ) { // //OWNERSAY("SET_VAL INSERT RECORD=["+(string)i+"] DBKEY=["+dbkey+"] DESC=["+field+"] VAL=["+val+"]"); // llSetLinkPrimitiveParamsFast(i,[PRIM_NAME,dbkey,PRIM_DESC,field,PRIM_TEXT,val,ZERO_VECTOR,0.0]); // written = TRUE; // we did an update, remember it // } // } // if ( written == TRUE ) { // //OWNERSAY("SET_VAL INSERT COMPLETED."); // return; // } // //OWNERSAY("SET_VAL NO FREE RECORD FOUND TO INSERT INTO! DATA LOST"); //} ////////////////////////////////////////////////////////////////////////////// // FLAG FUNCTIONS // INCAPACITATED - lost wounds // DEAD - lost critical wounds // DEBUG - show debug messages or not // IDEA: INDECISION - lost resolve? // LMIM: SET_FLAG|= // PPMA: FLAG,, // LMOUT: FLAG|= // LMERR: FIXME integer GET_FLAG(string flag) { string val = GET_VAL("FLAG",llToUpper(flag)); if ( val == KEY_NOT_FOUND ) { ERROR("Flag ["+flag+"] does not exist."); } if ( val != "0" && val != "1") { ERROR("Flag: "+flag+" invalid value ["+val+"]"); } integer bool = (integer)val; return bool; } // SET_FLAG // LMIM: SET_FLAG|= // PPMA: FLAG,, // LMOUT: SET_FLAG|= // LMERR: FIXME //SET_FLAG(string flag,integer value) { // if ( flag == "" ) return; // FIXME add error // if ( value != TRUE && value != FALSE ) return; // FIXME add err // SET_VAL("FLAG",llToUpper(flag),(string)value); //} // // GET_MYRIAD // Requires a MYRIAD CONSTANT NAME // Returns the amount of that Myriad rules constant or defaultif the player does not currently have that constant // integer GET_MYRIAD(string setting) { integer retval; string value = GET_VAL("MYRIAD",setting); if ( value == KEY_NOT_FOUND ) { if ( setting == "MINSTAT" ) retval = 1; if ( setting == "MAXSTAT" ) retval = 10; if ( setting == "MINSKILL" ) retval = 0; if ( setting == "MAXSKILL" ) retval = 7; if ( setting == "MINEFFECT" ) retval = 0; if ( setting == "MAXEFFECT" ) retval = 5; if ( setting == "MINRESILIENCE" ) retval = 0; if ( setting == "MAXRESILIENCE" ) retval = 20; if ( setting == "MINBOON" ) retval = 0; if ( setting == "MAXBOON" ) retval = 5; if ( setting == "MINFLAW" ) retval = 0; if ( setting == "MAXFLAW" ) retval = 5; if ( setting == "MINRP" ) retval = 0; if ( setting == "MAXRP" ) retval = 10; if ( setting == "MINITEM" ) retval = 0; if ( setting == "MAXITEM" ) retval = 100; if ( setting == "MINARMOR" ) retval = 0; if ( setting == "MAXARMOR" ) retval = 5; if ( setting == "MINDAMAGE" ) retval = 1; if ( setting == "MAXDAMAGE" ) retval = 5; ERROR("Unable to locate Myriad setting "+setting+" returning "+(string)retval); } else { retval = (integer)value; } return retval; } // // PPMA GET_NAME - returns player firstname or nickname for Talker/Emoter // string GET_NAME() { string defaultname = "Myriad Player"; string name = GET_VAL("CHARACTER","NAME"); list tokens = llParseString2List(name,[" ",".","_"],[]); string firstname = llList2String(tokens,0); string lastname = llList2String(tokens,1); string nickname = GET_VAL("CHARACTER","NICKNAME"); if ( name == KEY_NOT_FOUND && nickname == KEY_NOT_FOUND ) return defaultname; if ( name != KEY_NOT_FOUND && nickname == KEY_NOT_FOUND ) return name; if ( name == KEY_NOT_FOUND && nickname != KEY_NOT_FOUND ) return nickname; if ( name != KEY_NOT_FOUND && nickname != KEY_NOT_FOUND ) return firstname+" \""+nickname+"\" "+lastname; return defaultname; } // PPMA GET SPECIE string GET_SPECIE() { string specie = GET_VAL("SPECIE","SPECIE"); if ( specie == KEY_NOT_FOUND ) specie = "None"; return specie; } // PPMA GET BACKGROUND string GET_BACKGROUND() { string background = GET_VAL("BACKGROUND","BACKGROUND"); if ( background == KEY_NOT_FOUND ) background = "None"; return background; } // PPMA GET CAREER string GET_CAREER() { string career = GET_VAL("CAREER","CAREER"); if ( career == KEY_NOT_FOUND ) career = "None"; return career; } // PPMA GET TITLE string GET_TITLE() { string title = GET_VAL("CHARACTER","TITLE"); if ( title == KEY_NOT_FOUND ) title = ""; return title; } // PPMA GET FACTION string GET_FACTION() { string faction = GET_VAL("CHARACTER","FACTION"); if ( faction == KEY_NOT_FOUND ) faction = ""; return faction; } //============================================================================ // DEBUG - show debug chat with wearer name for sorting //============================================================================ DEBUG(string dmessage) { if ( GET_FLAG("DEBUG") == TRUE ) llMessageLinked(LINK_THIS,MODULE_METER,"DEBUG|"+dmessage,llGetOwner()); } //============================================================================ // ERROR - show errors on debug channel with wearer name for sorting //============================================================================ ERROR(string emessage) { llMessageLinked(LINK_THIS,MODULE_METER,"ERROR|"+emessage,llGetOwner()); } //============================================================================ // GET_RESILIENCE //============================================================================ integer GET_RESILIENCE(string name) { if ( name != "Wounds" && name != "CurrentWounds" && name != "CriticalWounds" && name != "CurrentCriticalWounds" && name != "Resolve" && name != "CurrentResolve") { ERROR("GET_RESILIENCE "+name+" invalid."); return -1; } string res = GET_VAL("RESILIENCE",name); if ( res == KEY_NOT_FOUND ) { ERROR("GET_RESILIENCE "+name+" not found in PPMA."); return -2; } integer val = (integer)res; if ( val < GET_MYRIAD("MINRESILIENCE") || val > GET_MYRIAD("MAXRESILIENCE") ) { ERROR("GET_RESILIENCE "+name+" invalid value "+res+" out of range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE")); return -3; } return val; } // MEMORY GET_MEMORY() { OWNERSAY(BASENAME+" free memory: "+(string)llGetFreeMemory()); // show this module's free memory info } // GETVERSION GET_VERSION() { OWNERSAY(BASENAME+" v"+VERSION+"-"+VERSIONDATE); // show this module's version info } //============================================================================ // METER - update a hovertext health meter or HUD bar graph //============================================================================ METER() { //if ( METERWORN == FALSE ) return; string name = GET_NAME(); integer curwounds = GET_RESILIENCE("CurrentWounds"); integer maxwounds = GET_RESILIENCE("Wounds"); integer curcritical = GET_RESILIENCE("CurrentCriticalWounds"); integer maxcritical = GET_RESILIENCE("CriticalWounds"); integer flag_incapacitated = GET_FLAG("INCAPACITATED"); integer flag_dead = GET_FLAG("DEAD"); string specie = GET_SPECIE(); string background = GET_BACKGROUND(); string career = GET_CAREER(); integer resolve = GET_RESILIENCE("Resolve"); integer curresolve = GET_RESILIENCE("CurrentResolve"); string title = GET_TITLE(); string faction = GET_FACTION(); // create a meter message packet string ownername = llList2String(llParseString2List(llKey2Name(llGetOwner()),["@"],[]),0); // strip @where from HG names string message = "METER"+"|"+ownername+"|"+name+"|"+(string)curwounds+"|"+(string)maxwounds+"|"+(string)curcritical+"|"+(string)maxcritical+"|"+(string)flag_dead+"|"+(string)flag_incapacitated+"|"+specie+"|"+background+"|"+career+"|"+(string)curresolve+"|"+(string)resolve+"|"+title+"|"+faction; llRegionSay(RENDEZVOUS1,message); // send the update to region for scorekeepers, etc llWhisper(CHANATTACH,message); // whisper to the wearer's actual meter llMessageLinked(LINK_THIS,MODULE_METER,message,llGetOwner()); // send meter updates to bus DEBUG("Wounds: "+(string)curwounds+" of "+(string)maxwounds+" Critical: "+(string)curcritical+" of "+(string)maxcritical+" Resolve: "+(string)curresolve+" of "+(string)resolve); } // // OWNERSAY - sent messages to player only // OWNERSAY(string message) { llMessageLinked(LINK_THIS,MODULE_METER,"OWNERSAY|"+message,llGetOwner()); } //============================================================================ // RESET //============================================================================ RESET() { llResetScript(); // now reset } //============================================================================ // SETUP - begin bringing the HUD online //============================================================================ SETUP() { CHANATTACH = (integer)("0x"+llGetSubString((string)llGetOwner(),1,7)); // attachment-specific channel OWNERSAY("Meter module active."); } //============================================================================ // DEFAULT STATE - load character sheet //============================================================================ default { // // EVENT: CHANGED // changed(integer changes) { if ( changes & CHANGED_REGION || changes & CHANGED_TELEPORT ) { 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_METER || sending_module == LM_SENDTOATTACHMENT ) return; // ignore our own link messages list fields = llParseString2List(str,["|"],[]); // break into list of fields based on "|"ider string command = llToLower(llStringTrim(llList2String(fields,0),STRING_TRIM)); // assume the first field is a Myriad Lite command if ( command == "debug" || command == "error" || command == "help" || command == "ownersay" || command == "rpevent" ) return; // ignore WELL commands // break down the rest of the message for this module //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 // General Myriad Module Commnads if ( command == "memory" ) { GET_MEMORY(); return;} // get version when needed if ( command == "reset" ) { RESET(); return;} // reset when told if ( command == "version" ) { GET_VERSION(); return;} // get version when needed // only debug module specific message events DEBUG("EVENT: link_message("+(string)sender+","+(string)sending_module+","+str+","+(string)id+")"); if ( command == "set_resilience" ) { // update meter when we see set resilience messages on the bus METER(); return; } if ( command == "character_loaded" ) { METER(); return; // we're out of notecard, so character sheet is loaded - start playing } if ( command == "attachmeter" ) { METERWORN = TRUE; // we need to send meter events METER(); // send update return; } if ( command == "detachmeter" ) { METERWORN = FALSE; return; } if ( command == "meter" ) { METER(); return; } } // STATE ENTRY state_entry() { // Check required character sheet data is in PPMA, if not, disable module until HUD reset // have to do this here, since state command cannot be called from global functions // See http://lslwiki.net/lslwiki/wakka.php?wakka=FunctionStateChangeHack // See also "Caveats" in http://wiki.secondlife.com/wiki/State list requirements = [ "RESILIENCE", "Wounds", "RESILIENCE", "CurrentWounds", "RESILIENCE", "CriticalWounds", "RESILIENCE", "CurrentCriticalWounds", "RESILIENCE", "Resolve", "RESILIENCE", "CurrentResolve", "MYRIAD", "MINRESILIENCE", "MYRIAD", "MAXRESILIENCE", "FLAG", "INCAPACITATED", "FLAG", "DEAD" ]; integer i; for ( i = 0; i < llGetListLength(requirements); i += 2 ) { string category = llList2String(requirements, i); string aspect = llList2String(requirements, i + 1); if ( GET_VAL(category,aspect) == KEY_NOT_FOUND ) { OWNERSAY("Required "+llToLower(category)+" \""+aspect+"\" not found. Meter module will not work correctly. Disabling..."); state disabled; } } SETUP(); } } // end default state state disabled { link_message(integer sender_num,integer sender,string message,key id) { if ( sender == MODULE_METER || sender == LM_SENDTOATTACHMENT ) return; // ignore our own messages list fields = llParseString2List(message,["|"],[]); // break line of text into = delimited fields string command = llToLower(llStringTrim(llList2String(fields,0),STRING_TRIM)); // field zero is the "command" if ( command == "debug" || command == "error" || command == "help" || command == "ownersay" || command == "rpevent" ) return; // ignore WELL commands if ( command == "reset" ) { RESET(); return; } } } // END