====== Myriad Lite Module Social Combat ====== // Myriad_Lite_Module_Social_Combat-v0.0.0-20130905.lsl // Copyright (c) 2013 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 FLAG INCAP DEAD // FIXME MORE RPVENT // FIXME MORE HELP // CONSTANTS - DO NOT CHANGE DURING RUN string BASENAME = "Myriad Lite Module Social Combat"; string VERSION = "0.0.0"; // script version string VERSIONDATE = "20130905"; // script yyyymmdd // Module to Module Messaging Constants integer MODULE_SOCIAL = -14; integer LM_SENDTOATTACHMENT = 0x80000000; integer RENDEZVOUS1; // FIXME Rendezvous1 chat sent to ALL Myriad players in region integer RENDEZVOUS2; // new Myriad Preview 7 regionwide dynamic channel // CONFIGURATION ITEMS string SOCIALATTACKDICE; // how many "dice" of damage does the social attack cause? FIXME CONFIG,SOCIALATTACKDICE, integer ISACTIVE; // is module active or not FIXME FLAG,SOCIALCOMBATACTIVE, // RUNTIME GLOBALS - CAN CHANGE DURING RUN string SOCIALATTACK; // which social attack skill is used, Deceit or Persuasion? // // 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; } // 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(6.0); // roll this d6 // // FIXME if 6 = burn six for SFX OR +1 Bonus Myriad p18 MyriadSE p24 // 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 } // DEBUG - show debug chat with wearer name for sorting DEBUG(string dmessage) { if ( GET_FLAG("DEBUG") == TRUE ) llMessageLinked(LINK_THIS,MODULE_SOCIAL,"DEBUG|"+dmessage,llGetOwner()); } // DECEIT SOCIAL COMBAT SKILL DECEIVE(string hitwho) { integer victimchan = (integer)("0x"+llGetSubString(hitwho,0,6)); // calculate victim's dynamic channel string attstat = (string)GET_STATISTIC("Intellect"); // FIXME PPMA get Attacker's Social Combat Attack Stat string attskill = (string)GET_SKILL("Deceit"); // FIXME PPMA get Attacker's Social Combat Attack Skill rank llRegionSay(victimchan,"DECEITATTACK"+"|"+attstat+"|"+attskill+"|"+SOCIALATTACKDICE+"|"+(string)llGetOwner()+"|"+"Deceit"); // make the attack! //SOCIALATTACK=""; // cleanup from the event } // ERROR - show errors on debug channel with wearer name for sorting ERROR(string emessage) { llMessageLinked(LINK_THIS,MODULE_SOCIAL,"ERROR|"+emessage,llGetOwner()); } // // GET_RESILIENCE // Requires a RESILIENCE NAME // Returns the amount of that resilience, or minimum if the player does not currently have that reslience // integer GET_RESILIENCE(string aresilience) { string val = GET_VAL("RESILIENCE",aresilience); if ( val == KEY_NOT_FOUND ) { ERROR("Unable to locate resilience "+aresilience+" returning "+(string)GET_MYRIAD("MINRESILIENCE")); return GET_MYRIAD("MINRESILIENCE"); } integer retval = (integer)val; if ( retval < GET_MYRIAD("MINRESILIENCE") || retval > GET_MYRIAD("MAXRESILIENCE") ) { ERROR("Resilience "+aresilience+" value "+val+" out of range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE")); return GET_MYRIAD("MINRESILIENCE"); } return retval; } // // GET_SKILL // Requires a SKILL NAME // Returns the rank value for that skill, or zero if player doesn't have skill // integer GET_SKILL(string askill) { string val = GET_VAL("SKILL",askill); if ( val == KEY_NOT_FOUND ) { ERROR("Unable to locate skill "+askill+" returning 0"); return 0; } integer retval = (integer)val; if ( retval < GET_MYRIAD("MINSKILL") || retval > GET_MYRIAD("MAXSKILL") ) { ERROR("Skill "+askill+" value "+val+" out of range "+(string)GET_MYRIAD("MINSKILL")+"-"+(string)GET_MYRIAD("MAXSKILL")); return GET_MYRIAD("MINSKILL"); } return retval; } // // GET_STAT // Requires a STAT NAME // Returns the rank value for that statistics, or zero if player doesn't have stat // integer GET_STATISTIC(string astat) { string val = GET_VAL("STATISTIC",astat); if ( val == KEY_NOT_FOUND ) { ERROR("Unabled to locate statistic "+astat+" returning 0"); return 0; } integer retval = (integer)val; if ( retval < GET_MYRIAD("MINSTAT") || retval > GET_MYRIAD("MAXSTAT") ) { ERROR("Statistic "+astat+" value "+val+" out of range "+(string)GET_MYRIAD("MINSTAT")+"-"+(string)GET_MYRIAD("MAXSTAT")); return GET_MYRIAD("MINSTAT"); } return retval; } // 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 } // An Opposed Ability Test - Myriad PDF p. 19 Myriad Special Edition p. 25 // Requires Attacker Attribute Name, Attacker Skill Name, Defender Attribute Name, Defender Skill Name // Returns TRUE for Success, FALSE for failure integer OPPOSED_TEST(integer aattrib,integer askill,integer dattrib,integer dskill) { integer acheck = ABILITY_TEST(aattrib,askill); // calculate attacker's ability test integer dcheck = ABILITY_TEST(dattrib,dskill); // calculate defender's ability test if ( acheck >= dcheck ) return TRUE; // attacker more than or equal to defender = attacker wins, ties go to attacker! return FALSE; // defender wins } // OWNERSAY OWNERSAY(string str) { llMessageLinked(LINK_THIS,MODULE_SOCIAL,"OWNERSAY|"+str,llGetOwner()); } // PERSUASION SOCIAL COMBAT SKILL PERSUADE(string hitwho) { integer victimchan = (integer)("0x"+llGetSubString(hitwho,0,6)); // calculate victim's dynamic channel string attstat = (string)GET_STATISTIC("Intellect"); // FIXME PPMA get Attacker's Social Combat Attack Stat string attskill = (string)GET_SKILL("Persuasion"); // FIXME PPMA get Attacker's Social Combat Attack Skill rank llRegionSay(victimchan,"PERSUASIONATTACK"+"|"+attstat+"|"+attskill+"|"+SOCIALATTACKDICE+"|"+(string)llGetOwner()+"|"+"Persuasion"); // make the attack! //SOCIALATTACK=""; // cleanup from the event } // RESET - shut down running animations then reset the script to reload character sheet RESET() { llResetScript(); // now reset } // RPEVENT RPEVENT(string rpevent) { llMessageLinked(LINK_THIS,MODULE_SOCIAL,"RPEVENT|"+rpevent,llGetOwner()); } // SETUP - begin bringing the HUD online SETUP() { SOCIALATTACKDICE = "1"; // just do one damage dice RENDEZVOUS1 = -999; // FIXME Rendezvous1 chat sent to ALL Myriad players in region list details = llGetParcelDetails(<0,0,0>,[PARCEL_DETAILS_ID]); string parcelid = llList2String(details,0); RENDEZVOUS2 = (integer)("0x"+llGetSubString(parcelid,0,7)); // FIXME - MODULE RESILIENCE SOCIAL ARMOR CLASS 1-5? // FIXME - MODULE RESILIENCE SOCIAL DAMAGE CLASS 1-5? // FIXME - MODULE RESILIENCE SOCIAL HEAL PARTIAL/HEAL ALL? SOCIALON(); // enable social combat by default } // SOCIALOFF - turn off social combat SOCIALOFF() { ISACTIVE = FALSE; OWNERSAY("Social Combat module inactive."); } // SOCIALON - turn on social combat SOCIALON() { ISACTIVE = TRUE; OWNERSAY("Social Combat module active."); } // DEFAULT STATE // FIXME - PERFORM SETUP AND REQUIRED SYSTEM SUPPORT HERE - MOVE TO RUNNING STATE WHEN COMPLETE? default { link_message(integer sender_num,integer sender,string message,key id) { if ( sender == MODULE_SOCIAL || 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 // only debug after ignored commands DEBUG("EVENT: link_message("+(string)sender_num+","+(string)sender+","+message+","+(string)id+")"); // 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 == "memory" ) { GET_MEMORY(); return; } if ( command == "reset" ) { RESET(); return; } if ( command == "version" ) { GET_VERSION(); return; } if ( command == "socialoff" ) { SOCIALOFF(); return; } if ( command == "socialon" ) { SOCIALON(); return; } if ( ISACTIVE == FALSE ) return; // if module is disabled, no need to go further // // Defend against an incoming Social Attack // if ( command == "deceitattack" || command == "persuasionattack" ) { // incoming social combat attack message? integer attackstat = llList2Integer(fields,1); // get attackers social combat attack stat (INTELLECT) integer attackskill = llList2Integer(fields,2); // get attackers social combat attack skill (PERSUASION) integer attackdice = llList2Integer(fields,3); // get attacker's social combat attack damage dice (1 or more with SFX bonuses) key owner = llList2Key(fields,4); // get attacker's/object's key // string item = llList2String(fields,5); // get attacker's/object's name // Is the attacker's attack stat value out of range? if ( attackstat < GET_MYRIAD("MINSTAT") || attackstat > GET_MYRIAD("MAXSTAT") ) { ERROR("Social Attack stat value "+(string)attackstat+" out of range: "+(string)GET_MYRIAD("MINSTAT")+"-"+(string)GET_MYRIAD("MAXSTAT")); RPEVENT("ignored an incoming social attack with attack stat value out of range!"); return; } // is the attack social combat attack skill value out of allowed range? if ( attackskill < GET_MYRIAD("MINSKILL") || attackstat > GET_MYRIAD("MAXSKILL") ) { ERROR("Social Attack skill value "+(string)attackskill+" out of range: "+(string)GET_MYRIAD("MINSKILL")+"-"+(string)GET_MYRIAD("MAXSKILL")); RPEVENT("ignored an incoming social attack with attack skill value out of range!"); return; } // is the attacking weapon's attack dice value out of allowed range? if ( attackdice < GET_MYRIAD("MINDAMAGE") || attackdice > GET_MYRIAD("MAXDAMAGE") ) { ERROR("Social Attack dice value out of range: "+(string)GET_MYRIAD("MINDAMAGE")+"-"+(string)GET_MYRIAD("MAXDAMAGE")); RPEVENT("ignored an incoming social attack with attack damage value out of range!"); return; } // // DEFEND AGAINST INCOMING ATTACK // if ( GET_RESILIENCE("CurrentResolve") <= 0 ) { // no resolve, you can't attack or defend social combat return; } integer defendstat = 0; // hold the defenders social combat stat amount integer defendskill = 0; // create a place to hold the defenders mortal combat skill rank defendstat = GET_STATISTIC("Spirit"); // get defenders social combat defense stat amount FIXME PPMA if ( command == "deceitattack" ) // FIXME defendskill = GET_SKILL("Deceit"); // get defenders social combat defense skill rank FIXME PPMA if ( command == "persuasionattack" ) // FIXME defendskill = GET_SKILL("Persuasion"); // get defenders social combat defense skill rank FIXME PPMA // // see if we're hit - Opposed Ability Test between Attacker Intellect+Persuasion vs. Defender Spirit+Persuasion // FIXME - add a variety of results based on Degree of Success through Degree of Failure // need a sliding scale of result messages based on how successful the attack or defense is // use a sliding scale of success, and multiple different messages to the user // "X is very persuasive" // "What X says makes sense" // "While you have misgivings, X's argument doesn't have any obvious holes in it" // "X is quite convincing" // enable SFX like THE PERFECT LIE here too integer amihit = OPPOSED_TEST(attackstat,attackskill,defendstat,defendskill); if ( amihit == TRUE ) { // we're hit! //if ( command == "deceitattack" ) { // OWNERSAY("Your RESOLVE may weaken under "+llKey2Name(owner)+"'s DECEIT!"); // RPEVENT("may lose RESOLVE under "+llKey2Name(owner)+"'s DECEIT!"); //} //if ( command == "persuasionattack" ) { // OWNERSAY("Your RESOLVE may weaken by "+llKey2Name(owner)+"'s PERSUASION!"); // RPEVENT("may lose RESOLVE from "+llKey2Name(owner)+"'s PERSUASION!"); //} // Show a generic "HIT" message to disguise if it was DECEIT or PERSUASION string ownername = llList2String(llParseString2List(llKey2Name(owner),["@"],[]),0); // strip @where from HG names OWNERSAY("You may be swayed by "+ownername); RPEVENT("may be swayed by "+ownername); llMessageLinked(LINK_THIS,MODULE_SOCIAL,"RESOLVEHIT|"+(string)attackdice,owner); // apply the hit } else { //if ( command == "deceitattack" ) { // OWNERSAY(llKey2Name(owner)+" attempts unsuccessfully to DECEIVE you!"); // RPEVENT("is not DECEIVED by "+llKey2Name(owner)); //} //if ( command == "persuasionattack" ) { // OWNERSAY(llKey2Name(owner)+" unsuccessfully attempts to PERSUADE you!"); // RPEVENT("is not PERSUADED by "+llKey2Name(owner)); //} // Show a generic "HIT" message to disguise if it was DECEIT or PERSUASION string ownername = llList2String(llParseString2List(llKey2Name(llGetOwner()),["@"],[]),0); // strip @where from HG names OWNERSAY(ownername+" attempts unsuccessfully to sway you!"); RPEVENT("is not swayed by "+ownername); } return; } // // Actions NOT Allowed When Dead/Incapacitated go below here // if ( GET_FLAG("DEAD") == TRUE || GET_FLAG("INCAPACITATED") == TRUE ) return; if ( GET_RESILIENCE("CurrentResolve") <= 0 ) { // cannot make social attack without some resolve OWNERSAY("You are unable to make a social attack without rallying some Resolve."); return; } // // Make a social "attack" check regionwide at targetplayer's channel // if ( command == "deceive" ) { SOCIALATTACK="DECEIT"; llSensor("",NULL_KEY,AGENT_BY_LEGACY_NAME,20,PI); // FIXME how to socially attack NPC? return; } if ( command == "persuade" ) { SOCIALATTACK="PERSUASION"; llSensor("",NULL_KEY,AGENT_BY_LEGACY_NAME,20,PI); // FIXME how to socially attack NPC? return; } } // end of link_message event // NO_SENSOR - social combat no_sensor() { if ( SOCIALATTACK == "DECEIT" ) { OWNERSAY("No one around to deceive..."); } if ( SOCIALATTACK == "PERSUASION" ) { OWNERSAY("No one around to persuade..."); } SOCIALATTACK=""; // clean up } sensor(integer num_detected) { //list who; //integer count; //for ( count = 0; count < num_detected; count++) { // who = who + llDetectedName(count); // SENSED = SENSED + llDetectedKey(count); //} //llDialog(llGetOwner(),"Socially attack who?",[who],CHANPLAYER); if ( SOCIALATTACK == "" ) return; // not a valid attack while(num_detected--) { key who = llDetectedKey(num_detected); // FIXME - if in mouselook and cursor pointing at 1 avatar, make TARGETED SOCIAL ATTACK else make AREA SOCIAL ATTACK // FIXME if in mouselook, focus the attack on that one person, with a bonus Gotta look them in the eye when you're lying LOL if ( SOCIALATTACK == "DECEIT" ) DECEIVE((string)who); //llMessageLinked(LINK_THIS,MODULE_HUD,"DECEIVE|1|"+(string)who+"|"+(string)llGetOwner(),who); if ( SOCIALATTACK == "PERSUASION" ) PERSUADE((string)who); //llMessageLinked(LINK_THIS,MODULE_HUD,"PERSUADE|1|"+(string)who+"|"+(string)llGetOwner(),who); } SOCIALATTACK=""; } // STATE ENTRY - called on Reset 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 = [ "STATISTIC","Intellect", "STATISTIC", "Spirit", "SKILL", "Deceit", "SKILL", "Persuasion", "RESILIENCE", "Resolve", "RESILIENCE", "CurrentResolve", "MYRIAD", "MINSTAT", "MYRIAD", "MAXSTAT", "MYRIAD", "MINSKILL", "MYRIAD", "MAXSKILL", "MYRIAD", "MINRESILIENCE", "MYRIAD", "MAXRESILIENCE", "MYRIAD", "MINDAMAGE", "MYRIAD", "MAXDAMAGE", "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. Social Combat module will not work correctly. Disabling..."); state disabled; } } // FIXME - OPTIONAL EFFECT: AURA OF TERROR // FIXME - OPTIONAL EFFECT: CHANGE THE SUBJECT // FIXME - OPTIONAL EFFECT: HUSKY TONES // FIXME - OPTIONAL EFFECT: THE PERFECT LIE // FIXME - OPTIONAL EFFECT: WITTICISM SETUP(); } } // end default state state disabled { link_message(integer sender_num,integer sender,string message,key id) { if ( sender == MODULE_SOCIAL || 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; } } } // SCRIPT END