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