// 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