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

Myriad Lite Module Resilience

// Myriad_Lite_Module_Resilience-v0.0.7-20131102.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 ADD RESOLVEHIT
// FIXME ADD MORE RPEVENT TO MODULE
// FIXME ADD MORE HELP TO MODULE
// FIXME WHY IS DEAD ANIMATION INFINITE?
// FIXME REGION SETTING DEATHPOINT
// FIXME REGION SETTING SPAWNPOINT
// FIXME REGION SETTING RESPAWNTIME
 
// CONSTANTS - DO NOT CHANGE DURING RUN
string BASENAME = "Myriad Lite Module Resilience";
string VERSION = "0.0.7"; // Allen Kerensky's script version
string VERSIONDATE = "20131102"; // Allen Kerensky's script yyyymmdd
 
// Module to Module Messaging Constants
integer MODULE_RESILIENCE = -8;
integer LM_SENDTOATTACHMENT = 0x80000000;
integer RENDEZVOUS1 = -999; // chat sent to ALL Myriad players in region
 
// RUNTIME GLOBALS - CAN CHANGE DURING RUN
key LASTHITBY; // UUID of the last person to HIT us
 
/////////////////////// REMOVE ALL THIS
// ARMOR
integer CURARMOR = 0; // highest armor value worn out of all armor worn, not a total
// FIXME MOVE THIS TO ARMOR AND SEND LINK MESSAGES
integer CHANATTACH; // dynamic channel for attachments
 
string ANIM_INCAPACITATED = "sleep"; // FIXME WELL anim when incapacitated
string ANIM_DEAD = "dead"; // FIXME WELL anim when dead
//integer FLAG_ANIMATE; // FIXME WELL
//vector  MOVELOCK = <0,0,0>; // FIXME WELL movelock position when incapacitated or dead
//float   TAU = 0.05; // FIXME WELL movelock tau
 
vector DEATHPOINT; // FIXME MOVE TO REGION SETTING OPENSIM ONLY coordinates to move to on death
vector RESPAWNPOINT; // FIXME MOVE TO REGION SETTING OPENSIM ONLY coordinates to move to on respawn
float RESPAWN_TIME = 30.0; // FIXME MOVE TO REGION SETTING time dead before automatic respawn
//
/////////////////////// END REMOVE ALL THIS
 
//////////////////////////////////////////////////////////////////////////////
// PPMA FUNCTIONS
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
// 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;
}
 
// GET_WOUNDS
integer GET_WOUNDS() {
    string woundval = GET_VAL("RESILIENCE","Wounds");
    if ( woundval == KEY_NOT_FOUND ) {
        ERROR("GET_WOUNDS unable to locate RESILIENCE,Wounds key in PPMA.");
        return -1;
    }
    integer wounds = (integer)woundval;
    if ( wounds < GET_MYRIAD("MINRESILIENCE") || wounds > GET_MYRIAD("MAXRESILIENCE") ) {
        ERROR("GET_WOUNDS value "+woundval+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
        return -2;
    }
    return wounds;
}
 
// SET_WOUNDS
//SET_WOUNDS(integer wounds) {
//    if ( wounds < GET_MYRIAD("MINRESILIENCE") || wounds > GET_MYRIAD("MAXRESILIENCE") ) {
//        ERROR("SET_WOUNDS value "+(string)wounds+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
//        return;
//    }
//    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"SET_RESILIENCE|Wounds="+(string)wounds,llGetOwner());    
//}
 
// GET_CURRENTWOUNDS
integer GET_CURRENTWOUNDS() {
    string woundval = GET_VAL("RESILIENCE","CurrentWounds");
    if ( woundval == KEY_NOT_FOUND ) {
        ERROR("GET_CURRENTWOUNDS unable to locate RESILIENCE,CurrentWounds key in PPMA.");
        return -1;
    }
    integer wounds = (integer)woundval;
    if ( wounds < GET_MYRIAD("MINRESILIENCE") || wounds > GET_MYRIAD("MAXRESILIENCE") ) {
        ERROR("GET_CURRENTWOUNDS value "+woundval+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
        return -2;
    }
    return wounds;
}
 
// SET_CURRENTWOUNDS
SET_CURRENTWOUNDS(integer curwounds) {
    if ( curwounds < GET_MYRIAD("MINRESILIENCE") || curwounds > GET_MYRIAD("MAXRESILIENCE") ) {
        ERROR("SET_CURRENTWOUNDS value "+(string)curwounds+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
        return;
    }
    // check if current <= max or only set to max
    integer max = GET_WOUNDS();
    if ( curwounds > max ) {
        ERROR("SET_CURRENTWOUNDS "+(string)curwounds+" greater than player max "+(string)max+", resetting to player max");
        curwounds = max;
    }
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"SET_RESILIENCE|CurrentWounds="+(string)curwounds,llGetOwner());    
}
 
// GET_CRITICALWOUNDS
integer GET_CRITICALWOUNDS() {
    string woundval = GET_VAL("RESILIENCE","CriticalWounds");
    if ( woundval == KEY_NOT_FOUND ) {
        ERROR("GET_CRITICALWOUNDS unable to locate RESILIENCE,CriticalWounds key in PPMA.");
        return -1;
    }
    integer wounds = (integer)woundval;
    if ( wounds < GET_MYRIAD("MINRESILIENCE") || wounds > GET_MYRIAD("MAXRESILIENCE") ) {
        ERROR("GET_CRITICALWOUNDS value "+woundval+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
        return -2;
    }
    return wounds;
}
 
// SET_CRITICALWOUNDS
//SET_CRITICALWOUNDS(integer critwounds) {
//    if ( critwounds < GET_MYRIAD("MINRESILIENCE") || critwounds > GET_MYRIAD("MAXRESILIENCE") ) {
//        ERROR("SET_CRITICALWOUNDS value "+(string)critwounds+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
//        return;
//    }
//    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"SET_RESILIENCE|CriticalWounds="+(string)critwounds,llGetOwner());    
//}
 
// GET_CURRENTCRITICALWOUNDS
integer GET_CURRENTCRITICALWOUNDS() {
    string woundval = GET_VAL("RESILIENCE","CurrentCriticalWounds");
    if ( woundval == KEY_NOT_FOUND ) {
        ERROR("GET_CURRENTCRITICAL WOUNDS unable to locate RESILIENCE,CurrentCriticalWounds key in PPMA.");
        return -1;
    }
    integer wounds = (integer)woundval;
    if ( wounds < GET_MYRIAD("MINRESILIENCE") || wounds > GET_MYRIAD("MAXRESILIENCE") ) {
        ERROR("GET_CURRENTCRITICALWOUNDS value "+woundval+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
        return -2;
    }
    return wounds;
}
 
// SET_CURRENTCRITICALWOUNDS
SET_CURRENTCRITICALWOUNDS(integer curcritwounds) {
    if ( curcritwounds < GET_MYRIAD("MINRESILIENCE") || curcritwounds > GET_MYRIAD("MAXRESILIENCE") ) {
        ERROR("SET_WOUNDS value "+(string)curcritwounds+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
        return;
    }
    // check if current <= max or only set to max
    integer max = GET_CRITICALWOUNDS();
    if ( curcritwounds > max ) {
        ERROR("SET_CURRENCRITICALWOUNDS "+(string)curcritwounds+" greater than player max "+(string)max+", resetting to player max");
        curcritwounds = max;
    }
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"SET_RESILIENCE|CurrentCriticalWounds="+(string)curcritwounds,llGetOwner());    
}
 
// GET_RESOLVE
integer GET_RESOLVE() {
    string resolveval = GET_VAL("RESILIENCE","Resolve");
    if ( resolveval == KEY_NOT_FOUND ) {
        ERROR("GET_RESOLVE unable to locate RESILIENCE,Resolve key in PPMA.");
        return -1;
    }
    integer resolve = (integer)resolveval;
    if ( resolve < GET_MYRIAD("MINRESILIENCE") || resolve > GET_MYRIAD("MAXRESILIENCE") ) {
        ERROR("GET_RESOLVE value "+resolveval+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
        return -2;
    }
    return resolve;
}
 
// SET_RESOLVE
//SET_RESOLVE(integer resolve) {
//    if ( resolve < GET_MYRIAD("MINRESILIENCE") || resolve > GET_MYRIAD("MAXRESILIENCE") ) {
//        ERROR("SET_RESOLVE value "+(string)resolve+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
//        return;
//    }
//    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"SET_RESILIENCE|Resolve="+(string)resolve,llGetOwner());    
//}
 
// GET_CURRENTRESOLVE
integer GET_CURRENTRESOLVE() {
    string resolveval = GET_VAL("RESILIENCE","CurrentResolve");
    if ( resolveval == KEY_NOT_FOUND ) {
        ERROR("GET_CURRENTRESOLVE unable to locate RESILIENCE,CurrentResolve key in PPMA.");
        return -1;
    }
    integer resolve = (integer)resolveval;
    if ( resolve < GET_MYRIAD("MINRESILIENCE") || resolve > GET_MYRIAD("MAXRESILIENCE") ) {
        ERROR("GET_CURRENTRESOLVE value "+resolveval+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
        return -2;
    }
    return resolve;
}
 
// SET_CURRENTRESOLVE
SET_CURRENTRESOLVE(integer resolve) {
    if ( resolve < GET_MYRIAD("MINRESILIENCE") || resolve > GET_MYRIAD("MAXRESILIENCE") ) {
        ERROR("SET_CURRENTRESOLVE value "+(string)resolve+" out of allowed range "+(string)GET_MYRIAD("MINRESILIENCE")+"-"+(string)GET_MYRIAD("MAXRESILIENCE"));
        return;
    }
    // check if current <= max or only set to max
    integer max = GET_RESOLVE();
    if ( resolve > max ) {
        ERROR("SET_CURRENTRESOLVE "+(string)resolve+" greater than player max "+(string)max+", resetting to player max");
        resolve = max;
    }
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"SET_RESILIENCE|CurrentResolve="+(string)resolve,llGetOwner());    
}
 
DEAD() {
    SET_FLAG("DEAD",TRUE); // remember that we're now dead
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STARTANIMATION|ANIMATION="+ANIM_DEAD,llGetOwner());
    //llStartAnimation(ANIM_DEAD); // start dead animation
    string hitname = llList2String(llParseString2List(llKey2Name(LASTHITBY),["@"],[]),0); // strip @where from HG names
    RPEVENT("has been killed by "+hitname+"!");
    OWNERSAY("You've been killed by "+hitname+"!");
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"DEAD",LASTHITBY);
    // FIXME if ( DEATHPOINT != ZERO_VECTOR ) osTeleportAgent(llGetKey(),DEATHPOINT);
    METER(); // update hover text
    llSetTimerEvent(RESPAWN_TIME); // respawn in a bit
}
 
// DEBUG - show debug chat with wearer name for sorting
DEBUG(string dmessage) {
    if ( GET_FLAG("DEBUG") == TRUE ) llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"DEBUG|"+dmessage,llGetOwner());
}
 
// ERROR - show errors on debug channel with wearer name for sorting
ERROR(string emessage) {
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"ERROR|"+emessage,llGetOwner());
}
 
// MEMORY
GET_MEMORY() {
    OWNERSAY(BASENAME+" free memory: "+(string)llGetFreeMemory());
}
 
// GETVERSION
GET_VERSION() {
    OWNERSAY(BASENAME+" v"+VERSION+"-"+VERSIONDATE);
}
 
// HEAL - restore lost WOUND and CRITICAL resilience
// Thanks to Artemis Tesla for contributing summary report logic
HEAL(integer healamount) {
    integer critsHealed = 0; // track how many crit boxes restored for summary report
    integer woundsHealed = 0; // track how many non-crit boxes restored for summary report
    integer reborn = FALSE; // track if reborn/respawn or not for summary report
    integer revived = FALSE;  // track of revived or not for summary report
    integer curwounds = GET_CURRENTWOUNDS();
    integer maxwounds = GET_WOUNDS();
    integer curcritical = GET_CURRENTCRITICALWOUNDS();
    integer maxcritical = GET_CRITICALWOUNDS();
 
    // TODO report once for multiple healing amounts
    while ( healamount-- ) {
        // step through each point of healing
        if ( curcritical < maxcritical ) { // is current critical less than max critical
            DEBUG("Heal one critical wound");
            curcritical++; // heal one current critical
            SET_CURRENTCRITICALWOUNDS(curcritical);
            critsHealed++; // add a point back
            if ( GET_FLAG("DEAD") == TRUE ) {   // healed a critical, critical now > 0 so not dead anymore
                SET_FLAG("DEAD",FALSE); // no longer dead
                llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STOPANIMATION|ANIMATION="+ANIM_DEAD,llGetOwner());
                //llStopAnimation(ANIM_DEAD);
                llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STARTANIMATION|ANIMATION="+ANIM_INCAPACITATED,llGetOwner());
                //llStartAnimation(ANIM_INCAPACITATED);
                llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"ALIVE",llGetOwner());
                METER();
                reborn = TRUE; // show rebirth in summary report
                DEBUG("Heal: reborn");
            }
        } else {
                if ( curwounds < maxwounds ) {   // player not critical, heal non-critical?
                    DEBUG("Heal one wound");
                    curwounds++; // add the healing point to current wounds
                    SET_CURRENTWOUNDS(curwounds);
                    woundsHealed++; // add a point of non-critical
                    if ( GET_FLAG("INCAPACITATED") == TRUE ) { // were they incapacitated?
                        SET_FLAG("INCAPACITATED",FALSE); // no longer gravely wounded
                        //llStopAnimation(ANIM_DEAD);
                        llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STOPANIMATION|ANIMATION="+ANIM_DEAD,llGetOwner());
                        //llStopAnimation(ANIM_INCAPACITATED);
                        llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STOPANIMATION|ANIMATION="+ANIM_INCAPACITATED,llGetOwner());
                        llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"REVIVED",llGetOwner());
                        METER();
                        revived = TRUE; // show revival in summary report
                        DEBUG("Heal: Revived!");
                        llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STOPMOVELOCK",llGetOwner());
                        //llStopMoveToTarget();
                        //MOVELOCK = <0,0,0>;
                    }
                } // end if curwounds < wounds
        }
    } // end while
 
    // Summary report of healing effects
    if ( critsHealed > 0 ) {   // was at least one critical healed?
        DEBUG("Critical Heal: "+(string)curcritical+" of "+(string)maxcritical+" critical wound boxes.");
        if (critsHealed > 1) { // was more then one critical wound healed?
            OWNERSAY("Critical " + (string)critsHealed + " wounds healed.");
        } else {
            OWNERSAY("Critical " + (string)critsHealed + " wound healed.");
        }
    }
    if (reborn == TRUE ) { // if player reborn from this heal
        RPEVENT("has been resurrected!");
        llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STOPANIMATION|ANIMATION="+ANIM_DEAD,llGetOwner());
        //if ( FLAG_ANIMATE == TRUE ) { // if we're allowed to change animations
        //    llStopAnimation(ANIM_DEAD); // stop "we're dead" animation
        //}
        OWNERSAY("You've been resurrected! Welcome back to the land of the living.");
    }
 
    if ( woundsHealed > 0 ) { // was at least 1 non-critical healed?
        DEBUG("Heal Non-Critical Wounds: "+(string)curwounds+" of "+(string)maxwounds+" non-critical wound boxes.");
        if (woundsHealed > 1) { // was more than one non-critical healed?
            OWNERSAY((string)woundsHealed + " non-critical wounds healed.");
        } else {
            OWNERSAY((string)woundsHealed + " non-critical wound healed.");
        }
    }
 
    if ( revived == TRUE ) { // if player revived from this heal
        RPEVENT("has revived and is no longer incapacitated!");
        llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STOPANIMATION|ANIMATION="+ANIM_INCAPACITATED,llGetOwner());
        //if ( FLAG_ANIMATE == TRUE ) { // if we're allowed to change anims
        //    llStopAnimation(ANIM_INCAPACITATED); // stop the "we're down" animation
        //}
        OWNERSAY("You are no longer incapacitated! Welcome back to the fight!");
    }
    METER(); // update hovertext
}
 
// HIT - player is hit - check to see if attack dice breach armor
// Making A Damage Roll (Myriad p25, Myriad Special Edition p31)
HIT(integer attackdice) {
    integer damagetaken = 0; // start with zero damage
    while(attackdice--) { // roll for each attack dice
        integer dieroll = 1+(integer)llFrand(6.0); // reasonably uniform d6
        if ( dieroll > CURARMOR ) { // attack roll stronger than armor worn?
            damagetaken++; // add a wound point
        }
    }
    // finished roll how did we do?
    if ( damagetaken > 0 ) { // we took damage
        if ( CURARMOR > 0 ) { // wearing armor? tell them it was breached
            OWNERSAY("That attack penetrated your armor and you've been wounded!");
            llWhisper(CHANATTACH,"ARMOREFFECTHIT");
        } else { // fighting in no armor?
            OWNERSAY("You've been wounded! Wear some armor next time?");
        }
        WOUNDED(damagetaken); // apply damage taken to resilences
    } else { // hit, but no damage taken
        // must be wearing *some* armor to be hit but avoid a wound, don't recheck for armor here
        OWNERSAY("Your armor blocked the damage from that attack!");
        llWhisper(CHANATTACH,"ARMOREFFECTBLOCKED");
    }
}
 
// INCAPACITATED - player lost all WOUNDS - unable to act
INCAPACITATED() {
    SET_FLAG("INCAPACITATED",TRUE); // yes, we're now incapacitated
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STARTMOVELOCK",llGetOwner());
    //if ( MOVELOCK == <0,0,0> ) MOVELOCK = llGetPos();
    //llMoveToTarget(MOVELOCK,TAU);
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"STARTANIMATION|ANIMATION="+ANIM_INCAPACITATED,llGetOwner());
    //llStartAnimation(ANIM_INCAPACITATED); // "we're hurt and down" animation
    RPEVENT("has been incapacitated!");
    OWNERSAY("You've been incapacitated!");
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"INCAPACITATED",llGetOwner());
    METER(); // update meter
    llSetTimerEvent(RESPAWN_TIME); // heal in a bit
}
 
// METER - update a hovertext health meter or HUD bar graph
METER() {
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"METER",llGetOwner());
}
 
//
// OWNERSAY
//
OWNERSAY(string msg) {
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"OWNERSAY|"+msg,llGetOwner());
}
 
//
// RECOVER - restore lost RESOLVE resilience
//
RECOVER(integer healamount) {
    integer resolvehealed = 0; // track how many non-crit boxes restored for summary report
    integer recovered = FALSE;  // track of revived or not for summary report
    integer curresolve = GET_CURRENTRESOLVE();
    integer maxresolve = GET_RESOLVE();
 
    // TODO report once for multiple healing amounts
    while ( healamount-- ) {
        // step through each point of healing
        if ( curresolve < maxresolve ) {   // do we need to heal non-critical resolve?
            DEBUG("Heal one resolve");
            curresolve++; // add the healing point to current resolve
            SET_CURRENTRESOLVE(curresolve);
            resolvehealed++; // add a point of resolve healed for report
            llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"RECOVERED",llGetOwner());
            recovered = TRUE; // show revival in summary report
        } // end if curwounds < wounds
    } // end while
 
    if (recovered == TRUE ) { // if player reborn from this heal
        RPEVENT("has recovered their Resolve.");
        OWNERSAY("You've recovered your Resolve.");
    }
 
    if ( resolvehealed > 0 ) { // was at least 1 non-critical healed?
        DEBUG("Heal Non-Critical Resolve: "+(string)curresolve+" of "+(string)maxresolve+" non-critical boxes.");
        if (resolvehealed > 1) { // was more than one non-critical healed?
            OWNERSAY((string)resolvehealed + " non-critical Resolve boxes healed.");
        } else {
            OWNERSAY((string)resolvehealed + " non-critical Resolve box healed.");
        }
    }
} 
 
// RESET - shut down running animations then reset the script to reload character sheet
RESET() {
    // do any cleanup tasks here before reset    
    llResetScript(); // now reset
}
 
//
// RESOLVEHIT - player is hit - check to see if attack dice breach armor
// Making A Damage Roll (Myriad p25, Myriad Special Edition p31)
RESOLVEHIT(integer attackdice) {
    integer damagetaken = 0; // start with zero damage
    while(attackdice--) { // roll for each attack dice
        //integer dieroll = 1+(integer)llFrand(6.0); // FIXME SOCIAL ARMOR? reasonably uniform d6
        //if ( dieroll > CURARMOR ) { // FIXME SOCIAL ARMOR? attack roll stronger than armor worn?
            damagetaken++; // add a wound point
        //}
    }
    // finished roll how did we do?
    if ( damagetaken > 0 ) { // we took damage
        // if ( CURARMOR > 0 ) { // wearing armor? tell them it was breached
        //    OWNERSAY("That attack penetrated your armor and you've been wounded!");
        //    llWhisper(CHANATTACH,"ARMOREFFECTHIT");
        //} else { // fighting in no armor?
        //}
 
        OWNERSAY("You've lost some of your resolve to continue. Can you rally?");            
        RESOLVELOST(damagetaken); // apply damage taken to resilences
    }// else { // hit, but no damage taken
    //    // must be wearing *some* armor to be hit but avoid a wound, don't recheck for armor here
    //    OWNERSAY("Your armor blocked the damage from that attack!");
    //    llWhisper(CHANATTACH,"ARMOREFFECTBLOCKED");
    //}
}
 
//
// RESOLVELOST - Player takes Resolve Resilience damage
//
RESOLVELOST(integer amount) {
    while (amount--) { // for each wound taken
        integer curresolve = GET_CURRENTRESOLVE();
        //integer curwounds = GET_CURRENTWOUNDS();
        //integer curcritical = GET_CURRENTCRITICALWOUNDS();
        //integer maxcritical = GET_CRITICALWOUNDS();
        //if ( curwounds > 0 && curcritical != maxcritical ) {
        //    llSay(DEBUG_CHANNEL,"ERROR! WOUND STATE IS NONSENSE! CANNOT APPLY DAMAGE!");
        //    OWNERSAY("ERROR! WOUND STATE IS NONSENSE! CANNOT APPLY DAMAGE! TELL ALLEN KERENSKY!");
        //    return;
        //}
        if ( curresolve > 1 ) { // resolve boxes left?
            curresolve--; // scratch off one
            SET_CURRENTRESOLVE(curresolve);
            RPEVENT("has lost some of the resolve to continue.");
        } else { // out of resolve, out of the social combat
            curresolve = 0; // force zero
            SET_CURRENTRESOLVE(curresolve);
            string hitbyname = llList2String(llParseString2List(llKey2Name(LASTHITBY),["@"],[]),0); // strip @where from HG names
            RPEVENT("has lost all resolve and been defeated by "+hitbyname+"!");
            OWNERSAY("You've lost all resolve and been defeated by "+hitbyname+"!");
            llSetTimerEvent(RESPAWN_TIME); // respawn in a bit
        }
    } // end while
}
 
// RPEVENT
// FIXME - change all RPEVENT to link message for main HUD to send?
RPEVENT(string rpevent) {
    llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"RPEVENT|"+rpevent,llGetOwner());
}
 
// SETUP - begin bringing the HUD online
SETUP() {
 
    SET_FLAG("INCAPACITATED",FALSE); // FIXME set them to default in setup, need a function to scan PPMA-stored resilience and calculate at setup based on values rather than a default
    SET_FLAG("DEAD",FALSE); // FIXME set them to default in setup, need a function to scan PPMA-stored resilience and calculate at setup based on values rather than a default
 
    CHANATTACH = (integer)("0x"+llGetSubString((string)llGetOwner(),1,7)); // attachment-specific channel
    llRegionSay(RENDEZVOUS1,"GET_REGION_SETTING|DEATHPOINT");
    llRegionSay(RENDEZVOUS1,"GET_REGION_SETTING|RESPAWNPOINT");
    //llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION);
    OWNERSAY("Resilience module active.");
}
 
// WOUNDED - Player takes Resilience damage
WOUNDED(integer amount) {
    while (amount--) { // for each wound taken
        integer curwounds = GET_CURRENTWOUNDS();
        integer curcritical = GET_CURRENTCRITICALWOUNDS();
        integer maxcritical = GET_CRITICALWOUNDS();
        if ( curwounds > 0 && curcritical != maxcritical ) {
            llSay(DEBUG_CHANNEL,"ERROR! WOUND STATE IS NONSENSE! CANNOT APPLY DAMAGE!");
            OWNERSAY("ERROR! WOUND STATE IS NONSENSE! CANNOT APPLY DAMAGE! TELL ALLEN KERENSKY!");
            return;
        }
        if ( curwounds > 1 && curcritical == maxcritical ) { // wound boxes left?
            curwounds--; // scratch off one
            SET_CURRENTWOUNDS(curwounds);
        } else if ( curwounds > 0 && curcritical == maxcritical ) { // last wound box lost, now incapacitated
            curwounds = 0; // force to zero
            SET_CURRENTWOUNDS(curwounds);
            INCAPACITATED(); // show incapacitation
        } else if ( curwounds == 0 && curcritical > 1 ) { // out of wounds, but still have critical
            curwounds = 0; // force zero
            SET_CURRENTWOUNDS(curwounds);
            curcritical--; // force zero
            SET_CURRENTCRITICALWOUNDS(curcritical);            
        } else { // out of critical wounds too, dead!
            curwounds = 0; // force zero
            SET_CURRENTWOUNDS(curwounds);
            curcritical = 0; // force zero
            SET_CURRENTCRITICALWOUNDS(curcritical);
            DEAD(); // show death
        }
    } // end while
}
 
// DEFAULT STATE - load character sheet
default {
 
    // CHANGED - triggered for many changes to the avatar
    // TODO reload sim-specific settings on region change
    //changed(integer changes) {
    //    if ( changes & CHANGED_REGION || changes & CHANGED_TELEPORT ) {
    //        llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION);
    //    }
    //}
 
    link_message(integer sender_num,integer sender,string str,key id) {
        if ( sender == MODULE_RESILIENCE || sender == 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
 
        // finish processing module-related commands only
        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;} // show the memory info
        if ( command == "reset" ) { RESET(); return;} // reset wrapper
        if ( command == "version" ) { GET_VERSION(); return; } // show the version
 
        // only debug module related events...
         DEBUG("EVENT: link_message("+(string)sender_num+","+(string)sender+","+str+","+(string)id+")");
 
        if ( command == "armorcurrent" ) { // ARMORCURRENT|integer newcurrentarmor
            integer rating = llList2Integer(fields,1);
            if ( rating >= GET_MYRIAD("MINARMOR") && rating <= GET_MYRIAD("MAXARMOR") ) {
                CURARMOR = rating;
            }
            return;
        }
        if ( command == "hit") {
            integer attdice = llList2Integer(fields,1);
            if ( attdice >= GET_MYRIAD("MINDAMAGE") && attdice <= GET_MYRIAD("MAXDAMAGE") ) {
                LASTHITBY = id;
                HIT(attdice);
            }
            return;
        }
        if ( command == "resolvehit" ) {
            integer attdice = llList2Integer(fields,1);
            if ( attdice >= GET_MYRIAD("MINDAMAGE") && attdice <= GET_MYRIAD("MAXDAMAGE") ) {
                LASTHITBY = id;
                RESOLVEHIT(attdice);
            }
            return;
        }
        if ( command == "healpartial" ) {
            integer healpart = llList2Integer(fields,1);
            HEAL(healpart);
            return;
        }
        if ( command == "healfull" || command == "healall" ) { HEAL(20); return; } // FIXME HEAL ALL means 100 points of healing
        if ( command == "recoverpartial" ) {
            integer recoverpart = llList2Integer(fields,1);
            RECOVER(recoverpart);
            return;
        }
        if ( command == "recoverfull" || command == "recoverall" ) { RECOVER(20); return; } // FIXME RECOVER ALL means 20 points of healing
        if ( command == "region_setting" ) { // look for deathpoint and respawnpoint
            string attrib = llToLower(llStringTrim(llList2String(subfields,0),STRING_TRIM)); // find the boon name
            vector pos = llList2Vector(subfields,1); // find the boon rank value
            if ( attrib == "deathpoint" ) { DEATHPOINT = pos; }
            if ( attrib == "respawnpoint" ) { RESPAWNPOINT = pos; }
            return;
        }
        //COMMAND(str); // send to shared command processor for chat and link messages
        return;
    } // end of link_message event
 
    // 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 = [ "RESILIENCE","Wounds","RESILIENCE","CurrentWounds","RESILIENCE","CriticalWounds","RESILIENCE","CurrentCriticalWounds","RESILIENCE", "Resolve", "RESILIENCE", "CurrentResolve", "MYRIAD", "MINRESILIENCE", "MYRIAD", "MAXRESILIENCE", "MYRIAD","MINARMOR","MYRIAD","MAXARMOR", "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. Resilience module will not work correctly. Disabling...");
                state disabled;
            }
        }
 
        SETUP(); // show credits and start character sheet load
    }
 
    // TIMER - scheduled events
    timer() {
        // Respawn timer ended
        if ( GET_FLAG("DEAD") == TRUE ) { // if dead
            RPEVENT("respawns!");
            HEAL(20); // heal 20 points of damage to respawn
            //FIXME if ( RESPAWNPOINT != ZERO_VECTOR ) osTeleportAgent(llGetKey(),RESPAWNPOINT);
            if ( RESPAWNPOINT != ZERO_VECTOR ) llTeleportAgentHome(llGetOwner());
        }
        if ( GET_FLAG("INCAPACITATED") == TRUE ) { // if hurt
            HEAL(1); // heal 1 wound
        }
        integer curwounds = GET_CURRENTWOUNDS();
        integer maxwounds = GET_WOUNDS();
        integer curresolve = GET_CURRENTRESOLVE();
        integer maxresolve = GET_RESOLVE();
        if ( curresolve < maxresolve ) {
            RECOVER(1); // heal 1 resolve
        }
        if ( curwounds == maxwounds && curresolve == maxresolve ) { // fully healed?
            llSetTimerEvent(0.0); // stop timer
        }
    }
} // end state running
 
state disabled {
    link_message(integer sender_num,integer sender,string message,key id) {
        if ( sender == MODULE_RESILIENCE || 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
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