User:Allen Kerensky/Myriad Lite/Myriad Lite Module Skill Ranged Combat-v0.0.2-20120826.lsl
From OpenSimulator
< User:Allen Kerensky | Myriad Lite
Revision as of 14:16, 26 August 2012 by Allen Kerensky (Talk | contribs)
Myriad_Lite_Module_Skill_Ranged_Combat-v0.0.2-20120826.lsl
// Myriad_Lite_Module_Skill_Ranged_Combat-v0.0.2-20120826.lsl // Copyright (c) 2012 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/ // CONSTANTS - DO NOT CHANGE DURING RUN string VERSION = "0.0.2"; // script version string VERDATE = "20120826"; // script yyyymmdd integer MINSTAT = 1; // min value for statistics integer MAXSTAT = 5; // max human value for a statistic/attribute integer MINSKILL = 1; // min value for skill rank integer MAXSKILL = 5; // max value for skill rank integer MINDAMAGE = 1; // min attack dice for weapon integer MAXDAMAGE = 5; // max attack dice for weapon integer CHANMYRIAD = -999; // chat sent to ALL Myriad players in region string DIV = "|"; // message field divider // 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 MODULE_PROGRESS = -9; // integer MODULE_WELL = -10; // integer MODULE_METER = -11; integer LM_SENDTOATTACHMENT = 0x80000000; // RUNTIME GLOBALS - CAN CHANGE DURING RUN integer FLAG_DEBUG; // see debug messages? key PLAYERID = NULL_KEY; // cached player UUID string PLAYERNAME = ""; // cached player name string NAME = ""; // character name list STATISTICS = []; list SKILLS = []; // skills [ string SkillName, integer SkillRank ] integer FLAG_INCAPACITATED; // incapacitated by wounds? integer FLAG_DEAD; // killed by critical wounds? // ABILITY TEST // Requires ATTRIBUTE NAME, SKILL NAME // Returns the ability test score for use by success fail, opposed rolls, etc // See Myriad PDF page 18, Myriad Special Edition page 24 integer ABILITY_TEST(integer attribute,integer skill) { integer highroll = 0; // clear out the highest roll while( attribute-- ) { // roll a dice for each point of the attribute integer roll = 1+(integer)llFrand(5.0); // roll this d6 if ( roll > highroll) highroll = roll; // if this is highest roll so far, remember it } // finished rolling a dice for each point of the base attribute return highroll + skill; // now, return the total of highest dice roll + skill value } // COMBATOFF - turn off fist fighter COMBATOFF() { //llMessageLinked(LINK_THIS,MODULE_RANGED,"COMBATOFF",llGetOwner()); } // COMBATON - turn on fist fighter COMBATON() { //llMessageLinked(LINK_THIS,MODULE_RANGED,"COMBATON",llGetOwner()); } // 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 Ranged: "+dmessage); } } // DEBUGON - turn on the DEBUG flag DEBUGON() { FLAG_DEBUG = TRUE; // set debug flag TRUE llOwnerSay("Debug Mode Activated"); } // DEBUGOFF - turn off the DEBUG flag DEBUGOFF() { FLAG_DEBUG = FALSE; // set debug flag to FALSE llOwnerSay("Debug Mode Deactivated"); } // ERROR - show errors on debug channel with wearer name for sorting ERROR(string emessage) { llSay(DEBUG_CHANNEL,"ERROR ("+llKey2Name(PLAYERID)+") Module Ranged: "+emessage); } // GET SKILL RANK // Requires a SKILL NAME // Returns the rank value for that skill, or zero if player doesn't have skill integer GET_SKILL_RANK(string askill) { integer atskill = 0; // start with skill zero in case character does not have that skill integer skillpos = llListFindList(SKILLS,[askill]); // position of skill name in list 0-n, or -1 if not found if ( skillpos >= 0 ) { // skill name was somewhere in list atskill = llList2Integer(SKILLS,++skillpos); // move to next pos in list after skillname and get skill rank there if ( atskill >= MINSKILL && atskill <= MAXSKILL ) return atskill; // found skill with value in range, return it; } // fall through... return 0; // player has zero levels in skill } // GETSTAT integer GETSTAT(string name) { integer pos = llListFindList(STATISTICS,[name]); if ( pos >= 0 ) { return llList2Integer(STATISTICS,pos + 1); } return 0; } // GETVERSION GETVERSION() { SENDTOHUD("VERSION="+VERSION+DIV+"VERSIONDATE="+VERDATE+DIV+llGetObjectName()); } // MEMORY MEMORY() { DEBUG("Free Memory: "+(string)llGetFreeMemory()); } // An Opposed Ability Test - Myriad PDF p. 19 Myriad Special Edition p. 25 // Requires Attacker Attribute Name, Attacker Skill Name, Defender Attribute Name, Defender Skill Name // Returns TRUE for Success, FALSE for failure integer OPPOSED_TEST(integer aattrib,integer askill,integer dattrib,integer dskill) { integer acheck = ABILITY_TEST(aattrib,askill); // calculate attacker's ability test integer dcheck = ABILITY_TEST(dattrib,dskill); // calculate defender's ability test if ( acheck > dcheck ) return TRUE; // attacker more than defender = attacker wins return FALSE; // defender wins } // RESET - shut down running animations then reset the script to reload character sheet RESET() { llResetScript(); // now reset } // RPEVENT RPEVENT(string rpevent) { llRegionSay(CHANMYRIAD,"RPEVENT|"+NAME+" ("+PLAYERNAME+") "+rpevent); } // SENDTOHUD - send reponses to HUD as Link Messages SENDTOHUD(string str) { DEBUG("SENDTOHUD("+str+")"); llMessageLinked(LINK_THIS,LM_SENDTOATTACHMENT,str,PLAYERID); } // SETUP - begin bringing the HUD online SETUP() { FLAG_DEBUG = FALSE; PLAYERID = llGetOwner(); // remember the owner's UUID PLAYERNAME = llKey2Name(PLAYERID); // remember the owner's legacy name FLAG_INCAPACITATED = FALSE; FLAG_DEAD = FALSE; } // DEFAULT STATE - load character sheet default { link_message(integer sender_num,integer sender,string message,key id) { if ( sender == MODULE_RANGED || sender == LM_SENDTOATTACHMENT ) return; // ignore our own messages DEBUG("EVENT: link_message("+(string)sender_num+","+(string)sender+","+message+","+(string)id+")"); list fields = llParseString2List(message,[DIV],[]); // break line of text into = delimited fields string command = llToLower(llStringTrim(llList2String(fields,0),STRING_TRIM)); // field zero is the "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_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_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? if ( skillname == "Ranged Combat" ) { 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 == "combatoff" ) { COMBATOFF(); return; } if ( command == "combaton" ) { COMBATON(); return; } if ( command == "debugoff" ) { DEBUGOFF(); return; } if ( command == "debugon" ) { DEBUGON(); return; } if ( command == "reset" ) { RESET(); return; } if ( command == "memory" ) { MEMORY(); return; } if ( command == "version" ) { GETVERSION(); return; } if ( command == "dead" ) { FLAG_DEAD = TRUE; return; } if ( command == "alive" ) { FLAG_DEAD = FALSE; return; } if ( command == "incapacitated" ) { FLAG_INCAPACITATED = TRUE; return; } if ( command == "revived" ) { FLAG_INCAPACITATED = FALSE; return; } if ( command == "rangedhit" ) { // mortal combat attack message? integer attackstat = llList2Integer(fields,1); // get attackers stat integer attackskill = llList2Integer(fields,2); // get attackers skill integer attackdice = llList2Integer(fields,3); // get attacker object's attack dice key owner = llList2Key(fields,4); // get attacker object's key string item = llList2String(fields,5); // get attacker object name if ( attackstat < MINSTAT || attackstat > MAXSTAT ) { // is the attack stat value out of allowed range? ERROR("Attack stat value "+(string)attackstat+" out of range: "+(string)MINSTAT+"-"+(string)MAXSTAT); // TODO make a tattletale RP event? return; } if ( attackskill < MINSKILL || attackstat > MAXSKILL ) { // is the attack skill value out of allowed range? ERROR("Attack skill value "+(string)attackskill+" out of range: "+(string)MINSKILL+"-"+(string)MAXSKILL); // TODO make a tattletale RP event? return; } if ( attackdice < MINDAMAGE || attackdice > MAXDAMAGE ) { // is the attacking weapon's attack dice value out of allowed range? ERROR("Attack dice value out of range: "+(string)MINDAMAGE+"-"+(string)MAXDAMAGE); // TODO make a tattletale RP event? return; } integer skillamount = 0; // create a place to hold the defenders mortal combat skill rank skillamount = GET_SKILL_RANK("Ranged Combat"); // get ranged combat skill rank // see if we're hit integer amihit = OPPOSED_TEST(attackstat,attackskill,GETSTAT("Grace"),skillamount); // attacker power+skill vs. defender grace+skill if ( amihit == TRUE ) { // we're hit! if ( command == "HITCHECK" || command == "RANGEDHIT" ) { // we're hit by ranged attack llOwnerSay("You've been hit in ranged combat by "+llKey2Name(owner)+"'s "+item+"!"); RPEVENT("struck by "+llKey2Name(owner)+"'s "+item+" in ranged combat!"); } llMessageLinked(LINK_THIS,MODULE_RANGED,"HIT|"+(string)attackdice,PLAYERID); // apply the hit } return; } // Actions NOT Allowed When Dead/Incapacitated go below here if ( FLAG_DEAD == TRUE || FLAG_INCAPACITATED == TRUE ) return; if ( command == "attachranged" ) { // holding a ranged weapon rather than using fists // return; } if ( command == "detachranged" ) { // are we going back to fists? // return; } // If Your Bullet has hit, fire a hitcheck regionwide at targetplayer's channel if ( command == "rangedcombat" ) { integer attdice = llList2Integer(fields,1); // get attack dice of weapon used string hitwho = llList2String(fields,2); // get UUID of who we hit string bywho = llList2String(fields,3); // should be our own UUID string bywhat = llList2String(fields,4); // name of item we hit with (good for bullets/missiles) integer victimchan = (integer)("0x"+llGetSubString(hitwho,0,6)); // calculate victim's dynamic channel integer attskill = 0; // zero our attack skill attskill = GET_SKILL_RANK("Ranged Combat"); // get ranged combat skill level llRegionSay(victimchan,"RANGEDHIT"+DIV+(string)GETSTAT("Power")+DIV+(string)attskill+DIV+(string)attdice+DIV+bywho+DIV+bywhat); // attack! return; } // end if RANGEDCOMBAT/TOHIT } // end of link_message event // STATE ENTRY - called on Reset state_entry() { SETUP(); // show credits and start character sheet load } } // end state // END