// 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|<name>=<TRUE|FALSE>
// PPMA: FLAG,<flagname>,<TRUE|FALSE>
// LMOUT: FLAG|<flagname>=<TRUE|FALSE>
// 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|<flagname>=<TRUE|FALSE>
// PPMA: FLAG,<flagname>,<TRUE|FALSE>
// LMOUT: SET_FLAG|<flagname>=<TRUE|FALSE>
// 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