Allen Kerensky"It seems you've been living two lives ..."

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,<number 1-5>
integer ISACTIVE; // is module active or not FIXME FLAG,SOCIALCOMBATACTIVE,<true|false>
 
// 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|<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;
}
 
// 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
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
DokuWiki