User:Allen Kerensky/Myriad Lite Dev/Myriad Lite Module Resilience-v0.0.0-20120510.lsl
From OpenSimulator
< User:Allen Kerensky | Myriad Lite Dev
Revision as of 08:34, 20 May 2012 by Allen Kerensky (Talk | contribs)
Myriad_Lite_Module_Resilience-v0.0.0-20120510.lsl
// Myriad_Lite_Module_Resilience-v0.0.0-20120510.lsl // Copyright (c) 2012 By Allen Kerensky (OSG/SL) // The Myriad RPG System was designed, written, and illustrated by Ashok Desai // Myriad RPG licensed under the Creative Commons Attribution 2.0 UK: England and Wales // http://creativecommons.org/licenses/by/2.0/uk/ // Myriad Lite software Copyright (c) 2011-2012 by Allen Kerensky (OSG/SL) // Baroun's Adventure Machine Copyright (c) 2008-2011 by Baroun Tardis (SL) // Myriad Lite and Baroun's Adventure Machine licensed under the // Creative Commons Attribution-Share Alike-Non-Commercial 3.0 Unported // http://creativecommons.org/licenses/by-nc-sa/3.0/ // You must agree to the terms of this license before making any use of this software. // If you do not agree to this license, simply delete these materials. // There is no warranty, express or implied, for your use of these materials. // CONSTANTS - DO NOT CHANGE DURING RUN string VERSION = "0.0.0"; // Allen Kerensky's script version string VERSIONDATE = "20120510"; // Allen Kerensky's script yyyymmdd integer MINSTAT = 1; // min value for statistics integer MAXSTAT = 5; // max human value for a statistic/attribute integer MINRESILIENCE = 1; // min value for resilience integer MAXRESILIENCE = 20; // max value for resilience integer MINSKILL = 1; // min value for skill rank integer MAXSKILL = 5; // max value for skill rank integer CHANMYRIAD = -999; // chat sent to ALL Myriad players in region float RESPAWN_TIME = 30.0; // time dead before automatic respawn string DIV = "|"; // message field divider string ANIM_INCAPACITATED = "sleep"; // anim when incapacitated string ANIM_DEAD = "dead"; // anim when dead integer MAXARMOR = 5; // max legal armor rating // Module to Module Messaging Constants //integer MODULE_HUD = -1; //integer MODULE_CHARSHEET = -2; //integer MODULE_ARMOR = -3; //integer MODULE_BAM = -4; //integer MODULE_RUMORS = -5; //integer MODULE_CLOSE = -6; //integer MODULE_RANGED = -7; integer MODULE_RESILIENCE = -8; //integer LM_SENDTOATTACHMENT = 0x80000000; // RUNTIME GLOBALS - CAN CHANGE DURING RUN integer FLAG_DEBUG = FALSE; // see debug messages? key PLAYERID = NULL_KEY; // cached player UUID string PLAYERNAME = ""; // cached player name string NAME = ""; // character name string SPECIES = ""; // character species string BACKGROUND = ""; // character childhood history string CAREER = ""; // character career or faction list STATISTICS = []; list RESILIENCES = []; list CURRENT_RESILIENCES = []; list SKILLS = []; // skills [ string SkillName, integer SkillRank ] integer FLAG_ANIMATE; // integer FLAG_INCAPACITATED; // incapacitated by wounds? integer FLAG_DEAD; // killed by critical wounds? vector MOVELOCK = <0,0,0>; // movelock position when incapacitated or dead float TAU = 0.05; // movelock tau integer CURARMOR = 0; // highest armor value worn out of all armor worn, not a total integer METERWORN; // using meter? // FIXME MOVE THIS TO ARMOR AND SEND LINK MESSAGES integer CHANATTACH = 0; // dynamic channel for attachments // DEAD - player is dead, kill them and wait to respawn DEAD() { FLAG_DEAD = TRUE; // remember that we're now dead METER(); // update hover text llStartAnimation(ANIM_DEAD); // start dead animation RPEVENT("has been killed!"); llOwnerSay("You've been killed!"); llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"DEAD",PLAYERID); llSetTimerEvent(RESPAWN_TIME); // respawn in a bit } // DEBUG - show debug chat with wearer name for sorting DEBUG(string dmessage) { if ( FLAG_DEBUG == TRUE ) { // are we debugging? llSay(DEBUG_CHANNEL,"("+llKey2Name(PLAYERID)+") Module Resilience: "+dmessage); } } // DEBUGOFF - turn off the DEBUG flag DEBUGOFF() { DEBUG("Debug Mode Deactivated"); FLAG_DEBUG = FALSE; // set debug flag to FALSE } // DEBUGON - turn on the DEBUG flag DEBUGON() { FLAG_DEBUG = TRUE; // set debug flag TRUE DEBUG("Debug Mode Activated"); } // ERROR - show errors on debug channel with wearer name for sorting ERROR(string emessage) { llSay(DEBUG_CHANNEL,"ERROR ("+llKey2Name(PLAYERID)+"): "+emessage); } // GET_MAX_RESILIENCE integer GET_MAX_RESILIENCE(string name) { integer pos = llListFindList(RESILIENCES,[name]); if ( pos >= 0 ) { return llList2Integer(RESILIENCES,pos + 1); } return 0; } // GET_RESILIENCE integer GET_RESILIENCE(string name) { integer pos = llListFindList(CURRENT_RESILIENCES,[name]); if ( pos >= 0 ) { return llList2Integer(CURRENT_RESILIENCES,pos + 1); } return 0; } // 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_RESILIENCE("Wounds"); integer maxwounds = GET_MAX_RESILIENCE("Wounds"); integer curcritical = GET_RESILIENCE("Critical"); integer maxcritical = GET_MAX_RESILIENCE("Critical"); // 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_RESILIENCE("Critical",curcritical); critsHealed++; // add a point back if ( FLAG_DEAD == TRUE ) { // healed a critical, critical now > 0 so not dead anymore FLAG_DEAD = FALSE; // no longer dead llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"ALIVE",PLAYERID); 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_RESILIENCE("Wounds",curwounds); woundsHealed++; // add a point of non-critical if ( FLAG_INCAPACITATED == TRUE ) { // were they incapacitated? FLAG_INCAPACITATED = FALSE; // no longer gravely wounded llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"REVIVED",PLAYERID); revived = TRUE; // show revival in summary report DEBUG("Heal: Revived!"); 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? llOwnerSay("Critical " + (string)critsHealed + " wounds healed."); } else { llOwnerSay("Critical " + (string)critsHealed + " wound healed."); } } if (reborn == TRUE ) { // if player reborn from this heal RPEVENT("has been resurrected!"); if ( FLAG_ANIMATE == TRUE ) { // if we're allowed to change animations llStopAnimation(ANIM_DEAD); // stop "we're dead" animation } llOwnerSay("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? llOwnerSay((string)woundsHealed + " non-critical wounds healed."); } else { llOwnerSay((string)woundsHealed + " non-critical wound healed."); } } if ( revived == TRUE ) { // if player revived from this heal RPEVENT("has revived and is no longer incapacitated!"); if ( FLAG_ANIMATE == TRUE ) { // if we're allowed to change anims llStopAnimation(ANIM_INCAPACITATED); // stop the "we're down" animation } llOwnerSay("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(5.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 llOwnerSay("That attack penetrated your armor and you've been wounded!"); llWhisper(CHANATTACH,"ARMOREFFECTHIT"); } else { // fighting in no armor? llOwnerSay("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 llOwnerSay("Your armor blocked the damage from that attack!"); llWhisper(CHANATTACH,"ARMOREFFECTBLOCKED"); } } // INCAPACITATED - player lost all WOUNDS - unable to act INCAPACITATED() { FLAG_INCAPACITATED = TRUE; // yes, we're now incapacitated if ( MOVELOCK == <0,0,0> ) MOVELOCK = llGetPos(); llMoveToTarget(MOVELOCK,TAU); METER(); // update meter llStartAnimation(ANIM_INCAPACITATED); // "we're hurt and down" animation RPEVENT("has been incapacitated!"); llOwnerSay("You've been incapacitated!"); llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"INCAPACITATED",PLAYERID); llSetTimerEvent(RESPAWN_TIME); // heal in a bit } // METER - update a hovertext health meter or HUD bar graph METER() { //if ( METERWORN == FALSE ) return; integer curwounds = GET_RESILIENCE("Wounds"); integer maxwounds = GET_MAX_RESILIENCE("Wounds"); integer curcritical = GET_RESILIENCE("Critical"); integer maxcritical = GET_MAX_RESILIENCE("Critical"); // create a meter message packet string message = "METER"+DIV+PLAYERNAME+DIV+NAME+DIV+(string)curwounds+DIV+(string)maxwounds+DIV+(string)curcritical+DIV+(string)maxcritical+DIV+(string)FLAG_DEAD+DIV+(string)FLAG_INCAPACITATED; llRegionSay(CHANMYRIAD,message); // send the update to region for scorekeepers, etc llWhisper(CHANATTACH,message); // whisper to the wearer's actual meter llMessageLinked(LINK_THIS,MODULE_RESILIENCE,message,llGetOwner()); // send meter updates to bus DEBUG("Wounds: "+(string)curwounds+" of "+(string)maxwounds+" wound boxes. Critical: "+(string)curcritical+" of "+(string)maxcritical+" critical wound boxes."); } // RESET - shut down running animations then reset the script to reload character sheet RESET() { llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"RESET",llGetOwner()); llResetScript(); // now reset } // RPEVENT // FIXME - change all RPEVENT to link message for main HUD to send? RPEVENT(string rpevent) { llRegionSay(CHANMYRIAD,"RPEVENT|"+NAME+" ("+PLAYERNAME+") "+rpevent); } // SET_RESILIENCE SET_RESILIENCE(string name,integer value) { if ( value < 0 ) { return;} // out of range if ( value > 20 ) { return; } // out of range integer curpos = llListFindList(CURRENT_RESILIENCES,[name]); integer curval; integer maxval; if ( curpos >= 0 ) { curval = llList2Integer(CURRENT_RESILIENCES,curpos + 1); } else { // resilience not found return; } integer maxpos = llListFindList(RESILIENCES,[name]); if ( maxpos >=0 ) { maxval = llList2Integer(RESILIENCES,maxpos + 1); } else { // resilience not found return; } if ( value <= maxval) { CURRENT_RESILIENCES = llListReplaceList(CURRENT_RESILIENCES,[value],curpos + 1, curpos + 1); } } // SETUP - begin bringing the HUD online SETUP() { PLAYERID = llGetOwner(); // remember the owner's UUID PLAYERNAME = llKey2Name(PLAYERID); // remember the owner's legacy name CHANATTACH = (integer)("0x"+llGetSubString((string)PLAYERID,1,7)); // attachment-specific channel llRequestPermissions(PLAYERID,PERMISSION_TRIGGER_ANIMATION); } // VERSION SHOW_VERSION() { DEBUG("Version: "+VERSION+" Date: "+VERSIONDATE); } // WOUNDED - Player takes Resilience damage WOUNDED(integer amount) { while (amount--) { // for each wound taken integer curwounds = GET_RESILIENCE("Wounds"); integer curcritical = GET_RESILIENCE("Critical"); integer maxcritical = GET_MAX_RESILIENCE("Critical"); if ( curwounds > 0 && curcritical != maxcritical ) { llSay(DEBUG_CHANNEL,"ERROR! WOUND STATE IS NONSENSE! CANNOT APPLY DAMAGE!"); llOwnerSay("ERROR! WOUND STATE IS NONSENSE! CANNOT APPLY DAMAGE! TELL ALLEN KERENSKY!"); return; } if ( curwounds > 0 && curcritical == maxcritical ) { // wound boxes left? curwounds--; // scratch off one SET_RESILIENCE("Wounds",curwounds); METER(); // update llOwnerSay("You've been wounded!"); } else if ( curwounds < 1 && curcritical > 0 ) { // incapacitated curwounds = 0; // force to zero SET_RESILIENCE("Wounds",curwounds); curcritical--; // scratch off a critical wound box SET_RESILIENCE("Critical",curcritical); INCAPACITATED(); // show incapacitation } else if ( curwounds < 1 && curcritical < 1 ) { // out of critical wounds? curwounds = 0; // force zero SET_RESILIENCE("Wounds",curwounds); curcritical = 0; // force zero SET_RESILIENCE("Critical",curcritical); DEAD(); // show death } } // end while } // DEFAULT STATE - load character sheet default { // attach - when attached or detached from inventory or during login // FIXME is this needed in a module attach(key id) { id = NULL_KEY; // LSLINT RESET(); // force to go through state entry } // 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(PLAYERID,PERMISSION_TRIGGER_ANIMATION); } } // on_rez - when rezzed to ground or from inventory as attachment during login on_rez(integer params) { params = 0; // LSLINT RESET(); // force to go through state entry } link_message(integer sender_num,integer sender,string str,key id) { if ( sender == MODULE_RESILIENCE ) return; // ignore our own link messages DEBUG("Link Message: "+str); sender_num = 0; // LSLINT id = NULL_KEY; // LSLINT list fields = llParseString2List(str,[DIV],[]); // break into list of fields based on DIVider string command = llToLower(llStringTrim(llList2String(fields,0),STRING_TRIM)); // assume the first field is a Myriad Lite command 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 == "set_name" ) { NAME = llList2String(subfields,1); // set the name return; } if ( command == "set_species" ) { SPECIES = llList2String(subfields,1); // set the species; return; } if ( command == "set_background" ) { BACKGROUND = llList2String(subfields,1); // set the species; return; } if ( command == "set_career" ) { CAREER = llList2String(subfields,1); // set the species; return; } if ( command == "set_statistic" ) { string statname = llList2String(subfields,0); // find the boon name integer statrank = llList2Integer(subfields,1); // find the boon rank value // TODO how to verify stat names are valid? if ( statrank >= MINSTAT && statrank <= MAXSTAT ) { // rank valid? STATISTICS = [statname,statrank] + STATISTICS; // add statistic to list } else { // invalid, report it ERROR("STATISTIC "+statname+" rank "+(string)statrank+" value out of allowed range: "+(string)MINSTAT+"-"+(string)MAXSTAT); } return; } if ( command == "set_resilience" ) { string resname = llList2String(subfields,0); // find the boon name integer resrank = llList2Integer(subfields,1); // find the boon rank value // TODO how to verify resilience names are valid? if ( resrank >= MINRESILIENCE && resrank <= MAXRESILIENCE ) { // rank valid? RESILIENCES = [resname,resrank] + RESILIENCES; // add resilience to list CURRENT_RESILIENCES = [resname,resrank] + CURRENT_RESILIENCES; // add to current list too } else { // invalid, report it ERROR("RESILIENCE "+resname+" rank "+(string)resrank+" value out of allowed range: "+(string)MINRESILIENCE+"-"+(string)MAXRESILIENCE); } return; } if ( command == "set_skill" ) { string skillname = llList2String(subfields,0); // find the skill name integer skillrank = llList2Integer(subfields,1); // find the skill rank // TODO how to verify skill names are valid? if ( skillrank >= MINSKILL && skillrank <= MAXSKILL ) { // skill rank valid? SKILLS = [skillname,skillrank] + SKILLS; // add skill to list } else { // invalid, report it ERROR("SKILL "+skillname+" rank "+(string)skillrank+" value out of allowed range: "+(string)MINSKILL+"-"+(string)MAXSKILL); } return; } if ( command == "armorcurrent" ) { // ARMORCURRENT|integer newcurrentarmor integer rating = llList2Integer(fields,1); if ( rating >= 0 && rating <= MAXARMOR ) { CURARMOR = rating; } return; } if ( command == "debugoff" ) { DEBUGOFF(); return; } if ( command == "debugon" ) { DEBUGON(); return; } if ( command == "hit") { integer attdice = llList2Integer(fields,1); if ( attdice >= 1 && attdice <= 5 ) { HIT(attdice); } return; } if ( command == "healpartial" ) { integer healpart = llList2Integer(fields,1); HEAL(healpart); return; } if ( command == "healfull" ) { HEAL(100); return; } if ( command == "version" ) { SHOW_VERSION(); // show the version 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() { SETUP(); // show credits and start character sheet load } // TIMER - scheduled events timer() { // Respawn timer ended if ( FLAG_DEAD == TRUE ) { // if dead RPEVENT("respawns!"); llMessageLinked(LINK_THIS,MODULE_RESILIENCE,"ALIVE",PLAYERID); RESET(); // reset and reload character } if ( FLAG_INCAPACITATED == TRUE ) { // if hurt HEAL(1); // heal 1 wound } integer curwounds = GET_RESILIENCE("Wounds"); integer maxwounds = GET_MAX_RESILIENCE("Wounds"); if ( curwounds == maxwounds ) { // fully healed? llSetTimerEvent(0.0); // stop timer } } } // end state running // END