User:Allen Kerensky/Myriad Lite Dev/Myriad Lite-v0.1.6-20120501.lsl
From OpenSimulator
< User:Allen Kerensky | Myriad Lite Dev(Difference between revisions)
(created) |
Latest revision as of 16:10, 3 May 2012
[edit] Myriad_Lite-v0.1.6-20120501.lsl
// Myriad_Lite-v0.1.6-20120501.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.1.6"; // Allen Kerensky's script version string VERSIONDATE = "20120501"; // 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 integer CHANCOMMAND = 5; // chat sent by player to their meter 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 string CHAN_PREFIX = "0x"; // channel prefix for calculating dynamic channels 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 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 HANDMYRIAD = 0; // Myriad channel handle integer CHANPLAYER = 0; // dynamic channel to one player's UUID integer HANDPLAYER = 0; // player channel handle integer CHANOBJECT = 0; // dynamic channel to one object's UUID integer HANDCOMMAND = 0; // command channel handle integer HANDATTACH = 0; // attachment channel handle integer CHANATTACH = 0; // dynamic channel for attachments integer CHANBAM = 0; // dynamic channel for BAM quests integer HANDBAM = 0; // BAM channel update integer FLAG_ANIMATE; // integer FLAG_INCAPACITATED; // incapacitated by wounds? integer FLAG_DEAD; // killed by critical wounds? vector MOVELOCK; // movelock position when incapacitated or dead float TAU = 0.5; // movelock tau integer CURARMOR = 0; // highest armor value worn out of all armor worn, not a total integer METERWORN; // using meter? // DEBUG - show debug chat with wearer name for sorting DEBUG(string dmessage) { if ( FLAG_DEBUG == TRUE ) { // are we debugging? llSay(DEBUG_CHANNEL,"("+llKey2Name(PLAYERID)+") HUD: "+dmessage); } } // ERROR - show errors on debug channel with wearer name for sorting ERROR(string emessage) { llSay(DEBUG_CHANNEL,"ERROR ("+llKey2Name(PLAYERID)+"): "+emessage); } RPEVENT(string rpevent) { llRegionSay(CHANMYRIAD,"RPEVENT|"+NAME+" ("+PLAYERNAME+") "+rpevent); } integer GET_RESILIENCE(string name) { integer pos = llListFindList(CURRENT_RESILIENCES,[name]); if ( pos >= 0 ) { return llList2Integer(CURRENT_RESILIENCES,pos + 1); } return 0; } integer GET_MAX_RESILIENCE(string name) { integer pos = llListFindList(RESILIENCES,[name]); if ( pos >= 0 ) { return llList2Integer(RESILIENCES,pos + 1); } return 0; } 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); } } // 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"); } } // 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 } // INCAPACITATED - player lost all WOUNDS - unable to act INCAPACITATED() { FLAG_INCAPACITATED = TRUE; // yes, we're now incapacitated 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_HUD,"INCAPACITATED",PLAYERID); llSetTimerEvent(RESPAWN_TIME); // heal in a bit } // 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_HUD,"DEAD",PLAYERID); llSetTimerEvent(RESPAWN_TIME); // respawn in a bit } // 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_HUD,"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_HUD,"REVIVED",PLAYERID); revived = TRUE; // show revival in summary report DEBUG("Heal: Revived!"); llStopMoveToTarget(); } } // 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 } // 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 } // An Unopposed Ability Test - Myriad PDF p. 19, Myriad Special Edition p. 25 // Requires TargetNumber, Attribute Name, Skill Name // Returns TRUE for Success and False for Fail integer UNOPPOSED_TEST(integer targetnum,integer tattribute,integer tskill ) { integer check = ABILITY_TEST(tattribute,tskill); // calculate the player's ability test value if ( check >= targetnum ) return TRUE; // player won the ability test return FALSE; // player lost the ability test } // SETUP - begin bringing the HUD online SETUP() { CREDITS(); // show Myriad credits as required by the Creative Commons - Attribution license PLAYERID = llGetOwner(); // remember the owner's UUID PLAYERNAME = llKey2Name(PLAYERID); // remember the owner's legacy name llSetText("",<0,0,0>,0); // clear any previous hovertext llMessageLinked(LINK_THIS,MODULE_HUD,"BAMRESET",NULL_KEY); // reset the BAM module too llMessageLinked(LINK_THIS,MODULE_HUD,"ARMORRESET",PLAYERID); // send reset to armor module } // CREDITS comply with Myriad RPG Creative Common-Attribution legal requirement CREDITS() { llOwnerSay("The Myriad RPG System was designed, written, and illustrated by Ashok Desai."); llOwnerSay("RPG System licensed under the Creative Commons Attribution 2.0 UK: England and Wales."); llOwnerSay("Myriad Lite v"+VERSION+" "+VERSIONDATE+" Copyright (c) 2011 by Allen Kerensky (OSG/SL)"); llOwnerSay("Licensed under Creative Commons Attribution-Share Alike-Non-Commercial 3.0 Unported."); llMessageLinked(LINK_THIS,MODULE_HUD,"VERSION",llGetOwner()); } // RESET - shut down running animations then reset the script to reload character sheet RESET() { if ( FLAG_DEAD == TRUE || FLAG_INCAPACITATED == TRUE ) { // don't allow reset if already on respawn timer llOwnerSay("Cannot reset while incapacitated or dead. You will respawn in a few moments."); return; } llOwnerSay("Resetting Myriad Lite. Please wait..."); // stop all running animations if ( FLAG_ANIMATE == TRUE ) { // do we have permission to animate? list anims = llGetAnimationList(PLAYERID); // get list of current animations for owner integer animcount = llGetListLength(anims); // count the number of animations in the list while (animcount--) { // step from end of animation list to beginning llStopAnimation(llList2String(anims,animcount)); // stopping each animation } } llMessageLinked(LINK_THIS,MODULE_HUD,"ARMORRESET",PLAYERID); // send reset to armor module llMessageLinked(LINK_THIS,MODULE_HUD,"BAMRESET",PLAYERID); // reset the BAM module too llMessageLinked(LINK_THIS,MODULE_HUD,"RESET",PLAYERID); // send reset to armor module llResetScript(); // now reset } // 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_HUD,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."); } // DEBUGON - turn on the DEBUG flag DEBUGON() { FLAG_DEBUG = TRUE; // set debug flag TRUE llOwnerSay("Debug Mode Activated"); llMessageLinked(LINK_THIS,MODULE_HUD,"DEBUGON",llGetOwner()); } // DEBUGOFF - turn off the DEBUG flag DEBUGOFF() { FLAG_DEBUG = FALSE; // set debug flag to FALSE llOwnerSay("Debug Mode Deactivated"); llMessageLinked(LINK_THIS,MODULE_HUD,"DEBUGOFF",llGetOwner()); } // COMBATOFF - turn off fist fighter COMBATOFF() { llMessageLinked(LINK_THIS,MODULE_HUD,"COMBATOFF",llGetOwner()); } // COMBATON - turn on fist fighter COMBATON() { llMessageLinked(LINK_THIS,MODULE_HUD,"COMBATON",llGetOwner()); } // COMMAND - process chat and link message commands together COMMAND(string msg) { // break down the commands and messages into units we can work with list fields = llParseString2List(msg,[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 if ( command == "checkammo" ) { CHECKAMMO(); return;} // check ammo in weapons if ( command == "combatoff") { COMBATOFF(); return; } // turn off fist fighter if ( command == "combaton" ) { COMBATON(); return; } // turn on the fist fighter if ( command == "credits" ) { CREDITS(); return;} // show the credits including version number if ( command == "debugoff" ) { DEBUGOFF(); return; } // player turn off debugging if ( command == "debugon" ) { DEBUGON(); return;} // player turn on debugging if ( command == "drawboth" ) { DRAW("both"); return; } // draw both weapons if ( command == "drawleft" ) { DRAW("left"); return; } // draw weapon in left hand if ( command == "drawright" ) { DRAW("right"); return; } // draw weapon using right hand if ( command == "holsterboth" ) { HOLSTER("both"); return; } // holster both weapons if ( command == "holsterleft" ) { HOLSTER("left"); return; } // holster weapon in left hand if ( command == "holsterright" ) { HOLSTER("right"); return; } // holster weapon in right hand if ( command == "quest" ) { QUEST(); return; } // check our current quest status if ( command == "reload" ) { RELOAD(); return;} // reload weapons if ( command == "reset" ) { RESET(); return;} // reset HUD //if ( command == "rumor" ) { RUMOR(msg); return;} // rumors if ( command == "safetyoff" ) { SAFETYOFF(); return;} // unsafe the weapons if ( command == "safetyon" ) { SAFETYON(); return;} // safe the weapons if ( command == "sheatheboth" ) { SHEATHE("both"); return; } // sheathe both weapons if ( command == "sheatheleft" ) { SHEATHE("left"); return; } // sheathe weapon in left hand if ( command == "sheatheright" ) { SHEATHE("right"); return; } // sheathe weapon in right hand if ( command == "version" ) { CREDITS(); return;} // show the credits including version number if ( llGetSubString(llStringTrim(llToLower(command),STRING_TRIM),0,4) == "armor" ) { SENDTOMODULE(msg,PLAYERID); return; } if ( llGetSubString(llStringTrim(llToLower(command),STRING_TRIM),0,4) == "rumor" ) { RUMOR(msg); return; } } SENDTOMODULE(string msg,key speaker) { DEBUG("SENDTOMODULE("+msg+")"); llMessageLinked(LINK_THIS,MODULE_HUD,msg,speaker); } SENDTOATTACHMENT(string msg) { DEBUG("SENDTOATTACHMENT("+msg+")"); llWhisper(CHANATTACH,msg); } // RUMOR CONTROL RUMOR(string cmdrumor) { DEBUG("Sending to rumor module: "+cmdrumor); llMessageLinked(LINK_THIS,MODULE_HUD,cmdrumor,PLAYERID); // relay rumor commands to module } // QUEST STATUS QUEST() { llMessageLinked(LINK_THIS,MODULE_HUD,"BAMSTATUS",PLAYERID); // send a status request to BAM Modules } // DRAW weapons DRAW(string hand) { if ( hand == "left" ) { llWhisper(CHANATTACH,"DRAWLEFT"); return; } // draw left-hand weapon if ( hand == "right" ) { llWhisper(CHANATTACH,"DRAWRIGHT"); return; } // draw right-hand weapon if ( hand == "both" ) { llWhisper(CHANATTACH,"DRAWBOTH"); return; } // draw both weapons } // SHEATHE weapons SHEATHE(string hand) { if ( hand == "left" ) { llWhisper(CHANATTACH,"SHEATHELEFT"); return; } // sheathe left-hand weapon if ( hand == "right" ) { llWhisper(CHANATTACH,"SHEATHERIGHT"); return; } // sheathe right-hand weapon if ( hand == "both" ) { llWhisper(CHANATTACH,"SHEATHEBOTH"); return; } // sheathe both weapons } // HOLSTER weapons HOLSTER(string hand) { if ( hand == "left" ) { llWhisper(CHANATTACH,"HOLSTERLEFT"); return; } // holster left-hand weapon if ( hand == "right" ) { llWhisper(CHANATTACH,"HOLSTERRIGHT"); return; } // holster right-hand weapon if ( hand == "both" ) { llWhisper(CHANATTACH,"HOLSTERBOTH"); return; } // holster both weapons } // CHECK AMMO CHECKAMMO() { llWhisper(CHANATTACH,"CHECKAMMO"); } // RELOAD RELOAD() { llWhisper(CHANATTACH,"RELOAD"); } // SAFETY ON SAFETYON() { llWhisper(CHANATTACH, "SAFETYON"); } // SAFETY OFF SAFETYOFF() { llWhisper(CHANATTACH, "SAFETYOFF"); } // DEFAULT STATE - load character sheet default { // STATE ENTRY - called on Reset state_entry() { SETUP(); // show credits and start character sheet load } // 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 } // attach - when attached or detached from inventory or during login attach(key id) { id = NULL_KEY; // LSLINT RESET(); // force to go through state entry } link_message(integer sender_num,integer sender,string data,key id) { if ( sender == MODULE_HUD ) return; // ignore our own messages DEBUG("HUD Link Message: "+data); sender_num = 0; // LSLINT id = NULL_KEY; // LSLINT list FIELDS = llParseString2List(data,["|"],[]); // break line of text into = delimited fields string CMD = 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 ( CMD == "SET_NAME" ) { NAME = llList2String(SUBFIELDS,1); // set the name return; } if ( CMD == "SET_SPECIES" ) { SPECIES = llList2String(SUBFIELDS,1); // set the species; return; } if ( CMD == "SET_BACKGROUND" ) { BACKGROUND = llList2String(SUBFIELDS,1); // set the species; return; } if ( CMD == "SET_CAREER" ) { CAREER = llList2String(SUBFIELDS,1); // set the species; return; } if ( CMD == "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 ( CMD == "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 ( CMD == "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 ( CMD == "CHARACTER_LOADED" ) { state running; // we're out of notecard, so character sheet is loaded - start playing } } // end of link_message event } // end default state // STATE RUNNING - character sheet loaded - player is active in the game state running { // STATE ENTRY state_entry() { llOwnerSay("Character Sheet loaded. You are now ready to roleplay."); if ( HANDMYRIAD != 0 ) llListenRemove(HANDMYRIAD); HANDMYRIAD = llListen(CHANMYRIAD,"",NULL_KEY,""); // setup listener for Myriad RP events if ( HANDCOMMAND != 0 ) llListenRemove(HANDCOMMAND); HANDCOMMAND = llListen(CHANCOMMAND,"",PLAYERID,""); // listen to chat commands from owner CHANPLAYER = (integer)("0x"+llGetSubString((string)PLAYERID,0,6)); // calculate a player-specfic dynamic chat channel if ( HANDPLAYER != 0 ) llListenRemove(HANDPLAYER); HANDPLAYER = llListen(CHANPLAYER,"",NULL_KEY,""); // listen on the player dynamic chat channel CHANATTACH = (integer)("0x"+llGetSubString((string)PLAYERID,1,7)); // attachment-specific channel if ( HANDATTACH != 0 ) llListenRemove(HANDATTACH); HANDATTACH = llListen(CHANATTACH,"",NULL_KEY,""); // listen for messages from attachments CHANBAM = (integer)(CHAN_PREFIX + llGetSubString((string)PLAYERID,-7,-1)); if ( HANDBAM != 0 ) llListenRemove(HANDBAM); HANDBAM = llListen(CHANBAM,"",NULL_KEY,""); // start listener with listenremove handle llOwnerSay("Registering any Myriad Lite-compatible attachments..."); llWhisper(CHANATTACH,"REGISTERATTACHMENTS"); // ask for attachments on their dynamic channel llRequestPermissions(PLAYERID,PERMISSION_TRIGGER_ANIMATION|PERMISSION_TAKE_CONTROLS); // calculate player's dynamic BAM channel METER(); // update hovertext RUMOR("RUMOR_RESET"); // reset the rumor module to load new rumor server UUID if needed QUEST(); // update the BAM Module llOwnerSay("HUD startup complete. "+(string)llGetFreeMemory()+" bytes free."); } // ON_REZ - logged in with meter, or worn from inventory while running on_rez(integer param) { param = 0; // LSLINT RESET(); // a reset to reload character } // ATTACH - logged in with meter or worn from inventory/ground while running attach(key id) { id = NULL_KEY; // LSLINT RESET(); // a reset to reload character } // CHANGED - triggered for many changes to the avatar // TODO reload sim-specific settings on region change changed(integer changes) { if ( changes & CHANGED_INVENTORY ) { // inventory changed somehow? llOwnerSay("Inventory changed. Reloading."); RESET(); // saved a new character sheet? - reset and re-read it. } if ( changes & CHANGED_REGION || changes & CHANGED_TELEPORT ) { llRequestPermissions(PLAYERID,PERMISSION_TRIGGER_ANIMATION|PERMISSION_TAKE_CONTROLS); METER(); // update the meter after a shift } } // TOUCH_START - touch HUD for adventure update touch_start(integer total_number) { total_number = 0; // LSLINT string action = llGetLinkName(llDetectedLinkNumber(0)); // get name of prim clicked in link set if ( action != "" && action != llGetObjectName() ) { // someone clicked a named button prim on this linkset COMMAND(action); // try that prim name as a command return; } METER(); } // TIMER - scheduled events timer() { // Respawn timer ended if ( FLAG_DEAD == TRUE ) { // if dead RPEVENT("respawns!"); llMessageLinked(LINK_THIS,MODULE_HUD,"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 } } // LINK MESSAGE - commands to and from other prims in HUD link_message(integer sender,integer sending_module,string str, key id) { if ( sending_module == MODULE_HUD ) return; // ignore our own link messages DEBUG("HUD Link Message: "+str); sender = 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 if ( command == "armorcurrent" ) { // ARMORCURRENT|integer newcurrentarmor integer rating = llList2Integer(fields,1); if ( rating >= 0 && rating <= MAXARMOR ) { CURARMOR = rating; } return; } if ( command == "hit") { integer attdice = llList2Integer(fields,1); if ( attdice >= 1 && attdice <= 5 ) { HIT(attdice); } return; } if ( llGetSubString(command,0,4) == "armor" ) { SENDTOATTACHMENT(str); return; } // process armor messages if ( sending_module == LM_SENDTOATTACHMENT ) { SENDTOATTACHMENT(str); return; } // send module messages to attachments COMMAND(str); // send to shared command processor for chat and link messages return; } // LISTEN - the main Myriad Lite message processor for RP events and player commands listen(integer channel, string speakername, key speakerid, string message) { DEBUG("HUD Listen: channel=["+(string)channel+"] name=["+speakername+"] id=["+(string)speakerid+"] message=["+message+"]"); speakername = ""; // LSLINT // calculate the dynamic channel of who is speaking in case we need to return commands CHANOBJECT = (integer)(CHAN_PREFIX+llGetSubString((string)speakerid,0,6)); // break down the commands and messages into units we can work with list fields = llParseString2List(message,[DIV],[]); // break into list of fields based on DIVider string command = llList2String(fields,0); // assume the first field is a Myriad Lite command // --- PLAYER COMMAND CHANNEL if ( channel == CHANCOMMAND ) { // handle player chat commands COMMAND(message); // send to shared command processor for chat and link messages return; } // end of if channel == player commands // --- BAM CHANNEL if ( channel == CHANBAM ) { SENDTOMODULE(message,speakerid); // send BAM to Module return; } // end if channel BAMCHAN // --- Myriad Lite regionwide messages if ( channel == CHANMYRIAD ) { // handle Myriad system messages if ( command == "RPEVENT" ) { // Myriad Lite RPEVENT - roleplay events everyone might find interesting string oldname = llGetObjectName(); // save the current object name llSetObjectName("Myriad RP Event"); // change the object name to llOwnerSay(llList2String(fields,1)); // now tell the owner the rest of the RPEVENT| message llSetObjectName(oldname); // restore the HUD back to its original name return; } // end if RPEVENT return; } // end if channel == CHANMYRIAD // --- ATTACHMENT CHANNEL if ( channel == CHANATTACH ) { // handle the attachment commands if ( FLAG_DEAD == TRUE || FLAG_INCAPACITATED == TRUE ) return; // can't mess with attachments while down if ( llToLower(llGetSubString(llStringTrim(command,STRING_TRIM),0,4)) == "armor" ) { SENDTOMODULE(message,PLAYERID); return; } // process armor messages if ( command == "ATTACHMELEE" || command == "ATTACHRANGED" ) { // holding a weapon rather than using fists? llMessageLinked(LINK_THIS,MODULE_HUD,message,PLAYERID); return; } if ( command == "DETACHMELEE" || command == "DETACHRANGED" ) { // are we going back to fists? llMessageLinked(LINK_THIS,MODULE_HUD,message,PLAYERID); return; } if ( command == "ATTACHMETER" ) { METERWORN = TRUE; // we need to send meter events METER(); // send update return; } if ( command == "DETACHMETER" ) { METERWORN = FALSE; return; } } // --- CHANPLAYER if ( channel == CHANPLAYER ) { // handle player dynamic commands if ( command == "RPEVENT" ) { // Myriad Lite RPEVENT - roleplay events everyone might find interesting string oldname = llGetObjectName(); // save the current object name llSetObjectName("Myriad RP Event (Private)"); // change the object name to llOwnerSay(llList2String(fields,1)); // now tell the owner the rest of the RPEVENT| message llSetObjectName(oldname); // restore the HUD back to its original name return; } // end if RPEVENT // incoming message from rumor server? if ( llGetSubString(llToLower(llStringTrim(command,STRING_TRIM)),0,4) == "rumor" ) { llMessageLinked(LINK_THIS,MODULE_HUD,message,speakerid); // send message and key of speaker to rumors return; } if ( command == "UNOPPOSED_CHECK" ) { // object in sim wants a simple skill check integer targetnum = llList2Integer(fields,1); // what is unopposed check target num? integer tattrib = llList2Integer(fields,2); // target attribute integer tskill = llList2Integer(fields,3); // target skill UNOPPOSED_TEST(targetnum,tattrib,tskill); return; } // we've been hit and have to make an opposed ability test to avoid it if ( command == "CLOSEHIT" ) { llMessageLinked(LINK_THIS,MODULE_HUD,message,llGetOwner()); return; } if ( command == "RANGEDHIT" ) { // mortal combat attack message? llMessageLinked(LINK_THIS,MODULE_HUD,message,llGetOwner()); return; } // Heal Some Damage if ( command == "HEALPARTIAL" ) { // only a partial heal integer boxeshealed = llList2Integer(fields,1); // how many boxes are we healing? HEAL(boxeshealed); // heal X number of boxes METER(); // update return; } if ( command == "HEALFULL" ) { // full heal, reset state HEAL(100); // heal up to 100 damage METER(); // update return; } // Actions NOT Allowed When Dead/Incapacitated go below here if ( FLAG_DEAD == TRUE || FLAG_INCAPACITATED == TRUE ) return; // If Your Bullet has hit, fire a hitcheck regionwide at targetplayer's channel if ( command == "CLOSECOMBAT" ) { llMessageLinked(LINK_THIS,MODULE_HUD,message,llGetOwner()); return; } if ( command == "RANGEDCOMBAT" || command == "TOHIT" ) { llMessageLinked(LINK_THIS,MODULE_HUD,message,llGetOwner()); return; } } // end if channel CHANPLAYER } // end listen } // end state running // END