User:Allen Kerensky/Myriad Lite/Myriad Lite Module Character Sheet-v0.0.5-20120826.lsl
From OpenSimulator
< User:Allen Kerensky | Myriad Lite
Revision as of 15:39, 26 August 2012 by Allen Kerensky (Talk | contribs)
Myriad_Lite_Module_Character_Sheet-v0.0.5-20120826.lsl
// Myriad_Lite_Module_Character_Sheet-v0.0.5-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/ // VERSION CONTROL string VERSION = "0.0.5"; // Allen Kerensky's script version string VERSIONDATE = "20120826"; // Allen Kerensky's script yyyymmdd // CHARACTER SHEET STORAGE string CARDVERSION = "0.0.5"; // what card format version do we expect string NAME = ""; // character name string SPECIES = ""; // species template used for character string BACKGROUND = ""; // background template string CAREER = ""; // career template list STATISTICS = []; list RESILIENCES = []; list CURRENT_RESILIENCES = []; list RES_STATS = []; list RES_TYPES = []; list BOONS = []; // boons [ string BoonName, integer BoonRank ] list FLAWS = []; // flaws [ string FlawName, integer FlawRank ] list SKILLS = []; // skills [ string SkillName, integer SkillRank ] list MORTAL_EFFECTS = []; // special effects (SFX) [ string EffectName, integer EffectRank ] list SOCIAL_EFFECTS = []; // special effects (SFX) [ string EffectName, integer EffectRank ] list MAGIC_EFFECTS = []; // special effects (SFX) [ string EffectName, integer EffectRank ] list VEHICLE_EFFECTS = []; // special effects (SFX) [ string EffectName, integer EffectRank ] list STUNTS = []; // pre-set martial combat stunts TODO how will this work? list QUOTES = []; // pre-set social combat quotes TODO how will this work? list EQUIPMENT = []; // Equipment [ string ItemName, integer NumberCarried ] // Level-Based Progress integer XP = 0; // 0-2320 integer XPLEVEL = 1; // 1-30 integer GP = 0; // general points for point-based character builder integer STATPOOL = 0; // statistics point pool integer HEALTHPOOL = 0; // resilience point pool integer SKILLPOOL = 0; // skill point pool integer SFXPOOL = 0; // special effect ability point pool integer RP = 0; // resource point pool // Gradual Progress integer XPLEFT = 0; // unspent XP // Random Progress integer SKILL_INCREASES; // how many times have we increased skills, when 5 skills, get new skill list SKILLS_INCREASED = []; // [ SkillName, TimesFailed ] if TimesFailed = 5, increase skill and reset integer NEW_SKILLS; // number of new skills earned through RANDOM skill increases since last reset list STATS_INCREASED = []; // [ StatName, Times Advanced ] if TimesAdvanced = 5, increase stat and reset integer SIXES_BURNED; // number of sixes burned > ( 10 * #SFX you have ) then gain new SFX // MYRIAD CONSTANTS integer MINXP = 0; // min experience points integer MAXXP = 2320; // max experience points integer MIN_XPLEVEL = 1; // min XP level integer MAX_XPLEVEL = 30; // max XP level list XP_BY_LEVEL = [ 0,0,10,25,45,70,100,135,175,220,270,325,385,450,520,595,675,760,850,945,1045,1150,1260,1375,1495,1620,1750,1885,2025,2170,2320 ]; 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 MINBOON = 1; // min value for boon rank integer MAXBOON = 5; // max value for boon rank integer MINFLAW = 1; // min value for flaw rank integer MAXFLAW = 5; // max value for flaw rank integer MINSKILL = 1; // min value for skill rank integer MAXSKILL = 5; // max value for skill rank integer MINEFFECT = 1; // min value for special effect integer MAXEFFECT = 5; // max value for special effect integer MINEQUIPPED = 1; // min number of items player can carry integer MAXEQUIPPED = 100; // max number of items player can carry TODO: what about bullets? //integer MINDAMAGE = 1; // min attack dice for weapon //integer MAXDAMAGE = 5; // max attack dice for weapon // 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; // Runtimes integer FLAG_DEBUG; key PLAYERID; string DIV="|"; string ESTATE; // what estate does this region belong to - loaded from region server string PROGRESSION; // Progression Method: LEVEL, GRADUAL, or RANDOM integer CHANMYRIAD = -999; integer CHANOBJECT; integer HANDOBJECT; // Menu Handling integer MENU_TIMER; integer MENU_TIMEOUT = 30; integer MENU_CHANNEL; integer MENU_HANDLE; list MENU; // NOTECARD HANDLING list CHARACTERS; string CARD; string DEFAULT = "Myriad_Lite_Character_Sheet-Preview6.txt"; // character sheet notecard integer LINE = 0; // reading line number key QUERY = NULL_KEY; // track notecard queries // ADD_XP - Add a point of XP ADD_XP(key granterid) { key objectowner = llList2Key(llGetObjectDetails(granterid,[OBJECT_OWNER]),0); key regionowner = llList2Key(llGetParcelDetails(<0,0,0>,[PARCEL_DETAILS_OWNER]),0); if ( objectowner != regionowner ) { ERROR("ADD_XP called by object or player who is not region owner. ["+(string)objectowner+"]["+(string)regionowner+"]"); return; } DEBUG("Progression="+PROGRESSION+" XP="+(string)XP+" XPLEVEL="+(string)XPLEVEL); if ( PROGRESSION == "RANDOM" ) { ERROR("Unable to add XP in region using Random Progression"); return; } if ( XP < MAXXP ) { XP++; // add one to total XP if ( PROGRESSION == "LEVEL-BASED" ) { integer currentlevel = XPLEVEL; integer templevel = GET_LEVEL_BY_XP(XP); if ( templevel > currentlevel ) { XPLEVEL = templevel; llOwnerSay("LEVEL UP! Congratulations, you are now XP Level "+(string)XPLEVEL); RPEVENT("LEVEL UP! Congratulations, "+llKey2Name(llGetOwner())+" is now XP Level "+(string)XPLEVEL); LEVELUP(XPLEVEL); } return; } if ( PROGRESSION == "GRADUAL" ) { XPLEFT++; // add one to XP you can spend in gradual progress mode return; } llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"XP_CHANGED|XP="+(string)XP,llGetOwner()); DEBUG("Progression="+PROGRESSION+" XP="+(string)XP+" XPLEVEL="+(string)XPLEVEL); } else { ERROR("XP already maxed."); } } CALCULATE_LEVEL_BY_XP() { integer i; for ( i=1; i < llGetListLength(XP_BY_LEVEL); i++ ) { integer basexp = llList2Integer(XP_BY_LEVEL,i); if ( XP >= basexp ) { SET_XPLEVEL(i); } } } CHECK_CARDVERSION(string ncversion) { if ( ncversion != CARDVERSION ) { ERROR("Character sheet format "+ncversion+" found. Format version "+CARDVERSION+" expected. Please update character sheets to newer versions."); } } DEBUG(string debugmsg) { if ( FLAG_DEBUG == TRUE ) { // are we debugging? llSay(DEBUG_CHANNEL,"("+llKey2Name(PLAYERID)+") MODULE CHARSHEET DEBUG: "+debugmsg); } } DEL_PROGRESS(key id) { key objectowner = llList2Key(llGetObjectDetails(id,[OBJECT_OWNER]),0); key regionowner = llList2Key(llGetParcelDetails(<0,0,0>,[PARCEL_DETAILS_OWNER]),0); if ( objectowner != regionowner ) { ERROR("DEL_PROGRESS called by object or player who is not region owner. ["+(string)objectowner+"]["+(string)regionowner+"]"); return; } if ( PROGRESSION == "LEVEL-BASED" ) { STATPOOL = 0; HEALTHPOOL = 0; SKILLPOOL = 0; SFXPOOL = 0; return; } if ( PROGRESSION == "GRADUAL" ) { XPLEFT = 0; return; } if ( PROGRESSION == "RANDOM" ) { SKILL_INCREASES = 0; NEW_SKILLS = 0; SKILLS_INCREASED = []; STATS_INCREASED = []; SIXES_BURNED = 0; return; } } DUMP_SHEET(key id) { // dump current character sheet key objectowner = llList2Key(llGetObjectDetails(id,[OBJECT_OWNER]),0); key regionowner = llList2Key(llGetParcelDetails(<0,0,0>,[PARCEL_DETAILS_OWNER]),0); if ( objectowner != regionowner ) { ERROR("DUMP_SHEET called by object or player who is not region owner. ["+(string)objectowner+"]["+(string)regionowner+"]"); return; } DEBUG("Dumping character sheet to "+llKey2Name(id)+" ("+(string)id+")"); integer chan = (integer)("0x"+llGetSubString((string)id,0,6)); string tempname = llGetObjectName(); llSetObjectName(""); llSay(chan,"VERSION=0.0.3"); llSay(chan,"NAME|NAME="+NAME); // Species llSay(chan,"SPECIES|SPECIES="+SPECIES); // Backgrounds llSay(chan,"BACKGROUNDS|BACKGROUND="+BACKGROUND); // Careers llSay(chan,"CAREERS|CAREER="+CAREER); //llSay(chan,"XP="+(string)XP); //llSay(chan,"XPLEVEL="+(string)XPLEVEL); // Statistics integer numstats = llGetListLength(STATISTICS); integer count; for ( count = 0; count < numstats; count += 2 ) { string name = llList2String(STATISTICS,count); integer amount = llList2Integer(STATISTICS,count + 1); if ( amount > 0 ) llSay(chan,"STATISTIC|"+name+"="+(string)amount); } // Resiliences numstats = llGetListLength(RESILIENCES); for ( count = 0; count < numstats; count++) { string name = llList2String(RESILIENCES,count++); integer amount = llList2Integer(RESILIENCES,count); string base = llList2String(RES_STATS,llListFindList(RES_STATS,[name]) + 1); integer baseamt; if ( llList2String(RES_TYPES,llListFindList(RES_TYPES,[name]) + 1 ) == "Critical" ) { baseamt = llCeil( llList2Float(STATISTICS,llListFindList(STATISTICS,[base]) + 1) / 2 ); } else { baseamt = llList2Integer(STATISTICS,llListFindList(STATISTICS,[base]) + 1); } if ( ( amount ) > 0 ) llSay(chan,"RESILIENCE|"+name+"="+(string)(amount)); } // Boons numstats = llGetListLength(BOONS); for ( count = 0; count < numstats; count++) { string name = llList2String(BOONS,count++); integer amount = llList2Integer(BOONS,count); if ( amount > 0 ) llSay(chan,"BOON|"+name+"="+(string)amount); } // Flaws numstats = llGetListLength(FLAWS); for ( count = 0; count < numstats; count++) { string name = llList2String(FLAWS,count++); integer amount = llList2Integer(FLAWS,count); if ( amount > 0 ) llSay(chan,"FLAW|"+name+"="+(string)amount); } // Skills numstats = llGetListLength(SKILLS); for ( count = 0; count < numstats; count++) { string name = llList2String(SKILLS,count++); integer amount = llList2Integer(SKILLS,count); if ( amount > 0 ) llSay(chan,"SKILL|"+name+"="+(string)amount); } // Mortal Combat SFX numstats = llGetListLength(MORTAL_EFFECTS); for ( count = 0; count < numstats; count++) { string name = llList2String(MORTAL_EFFECTS,count++); integer amount = llList2Integer(MORTAL_EFFECTS,count); if ( amount > 0 ) llSay(chan,"MORTAL_EFFECT|"+name+"="+(string)amount); } // Social Combat SFX numstats = llGetListLength(SOCIAL_EFFECTS); for ( count = 0; count < numstats; count++) { string name = llList2String(SOCIAL_EFFECTS,count++); integer amount = llList2Integer(SOCIAL_EFFECTS,count); if ( amount > 0 ) llSay(chan,"SOCIAL_EFFECT|"+name+"="+(string)amount); } // Mortal Combat SFX numstats = llGetListLength(MAGIC_EFFECTS); for ( count = 0; count < numstats; count++) { string name = llList2String(MAGIC_EFFECTS,count++); integer amount = llList2Integer(MAGIC_EFFECTS,count); if ( amount > 0 ) llSay(chan,"MAGIC_EFFECT|"+name+"="+(string)amount); } // Mortal Combat SFX numstats = llGetListLength(VEHICLE_EFFECTS); for ( count = 0; count < numstats; count++) { string name = llList2String(VEHICLE_EFFECTS,count++); integer amount = llList2Integer(VEHICLE_EFFECTS,count); if ( amount > 0 ) llSay(chan,"VEHICLE_EFFECT|"+name+"="+(string)amount); } // Stunts llSay(chan,"STUNT|STUNT=FIXME"); // Quotes llSay(chan,"QUOTE|QUOTE=FIXME"); // Equipment numstats = llGetListLength(EQUIPMENT); for ( count = 0; count < numstats; count++) { string name = llList2String(EQUIPMENT,count++); integer amount = llList2Integer(EQUIPMENT,count); if ( amount > 0 ) llSay(chan,"EQUIPMENT|"+name+"="+(string)amount); } llSay(chan,"CHARACTER_LOADED"); llSetObjectName(tempname); } DUMP_PROGRESS(key id) { // id to dump progress back to key objectowner = llList2Key(llGetObjectDetails(id,[OBJECT_OWNER]),0); key regionowner = llList2Key(llGetParcelDetails(<0,0,0>,[PARCEL_DETAILS_OWNER]),0); if ( objectowner != regionowner ) { ERROR("DUMP_PROGRESS called by object or player who is not region owner. ["+(string)objectowner+"]["+(string)regionowner+"]"); return; } integer chan = (integer)("0x"+llGetSubString((key)id,0,6)); if ( PROGRESSION == "LEVEL-BASED" ) { llSay(chan,"PROGRESS|XP="+(string)XP); llSay(chan,"PROGRESS|XPLEVEL="+(string)XPLEVEL); llSay(chan,"PROGRESS|STATPOOL="+(string)STATPOOL); llSay(chan,"PROGRESS|HEALTHPOOL="+(string)HEALTHPOOL); llSay(chan,"PROGRESS|SKILLPOOL="+(string)SKILLPOOL); llSay(chan,"PROGRESS|SFXPOOL="+(string)SFXPOOL); return; } if ( PROGRESSION == "GRADUAL" ) { llSay(chan,"PROGRESS|XP="+(string)XP); llSay(chan,"PROGRESS|XPLEFT="+(string)XPLEFT); return; } if ( PROGRESSION == "RANDOM" ) { llSay(chan,"PROGRESS|SKILL_INCREASES="+(string)SKILL_INCREASES); llSay(chan,"PROGRESS|NEW_SKILLS="+(string)NEW_SKILLS); llSay(chan,"PROGRESS|SIXES_BURNED="+(string)SIXES_BURNED); integer count = llGetListLength(SKILLS_INCREASED); integer i; for ( i=0; i< count; i++) { llSay(chan,"PROGRESS|SKILL="+llList2String(SKILLS_INCREASED,i)+"|TIMESFAILED="+llList2String(SKILLS_INCREASED,i + 1)); } count = llGetListLength(STATS_INCREASED); for ( i=0; i< count; i++) { llSay(chan,"PROGRESS|STAT="+llList2String(STATS_INCREASED,i)+"|TIMESADVANCERD="+llList2String(STATS_INCREASED,i + 1)); } return; } } ERROR(string errmsg) { llSay(DEBUG_CHANNEL,"("+llKey2Name(PLAYERID)+") MODULE CHARSHEET ERROR: "+errmsg); } FIND_NOTECARD() { CHARACTERS = []; MENU = ["Default"]; //string regionname = llGetRegionName(); integer count = llGetInventoryNumber(INVENTORY_NOTECARD); while (count--) { string currentcard = llGetInventoryName(INVENTORY_NOTECARD,count); list tokens = llParseString2List(currentcard,["@"],[]); string cardname = llList2String(tokens,0); string cardestate = llList2String(tokens,1); if ( cardname != DEFAULT && cardestate == ESTATE ) { CHARACTERS = [cardname,currentcard] + CHARACTERS; MENU = [cardname] + MENU; DEBUG("Found estate-specific character sheet "+cardname+"@"+cardestate); } } MENU_CHANNEL = (integer)llFrand(9999.0) * -1; MENU_HANDLE = llListen(MENU_CHANNEL,"",llGetOwner(),""); llDialog(llGetOwner(),"Choose your character",MENU,MENU_CHANNEL); MENU_TIMER = MENU_TIMEOUT; llSetTimerEvent(1.0); } integer GET_LEVEL_BY_XP(integer amount) { integer count = 0; integer outlevel = 0; for (count = 0; count < MAX_XPLEVEL; count++ ) { if ( amount > llList2Integer(XP_BY_LEVEL,count) ) outlevel = count; } DEBUG("GET_LEVEL_BY_XP("+(string)amount+") returning "+(string)outlevel); return outlevel; } integer GET_MAXRESILIENCE(string name) { integer pos = llListFindList(RESILIENCES,[name]); if ( pos >= 0 ) { return llList2Integer(RESILIENCES,pos + 1); } return 0; } integer GET_RESILIENCE(string name) { integer pos = llListFindList(CURRENT_RESILIENCES,[name]); if ( pos >= 0 ) { return llList2Integer(CURRENT_RESILIENCES,pos + 1); } return 0; } integer GET_STAT(string name) { integer pos = llListFindList(STATISTICS,[name]); if ( pos >= 0 ) { return llList2Integer(STATISTICS,pos + 1); } return 0; } GETVERSION() { llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"VERSION_INFO"+DIV+"NAME="+llGetScriptName()+DIV+"VERSION="+VERSION+DIV+"VERSIONDATE="+VERSIONDATE,llGetOwner()); } integer GET_XP() { if ( XP >= MINXP && XP <= MAXXP) { // inform all other modules that might need to track XPLEVEL llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"GET_XP|XP="+(string)XP,llGetOwner()); return XP; } else { ERROR("GET_XP("+(string)XP+") AMOUNT OUT OF RANGE "+(string)MINXP+"-"+(string)MAXXP+"! RESETTING"); } RESET(); return 0; } integer GET_XPLEVEL() { if ( XPLEVEL >= MIN_XPLEVEL && XPLEVEL <= MAX_XPLEVEL) { // inform all other modules that might need to track XPLEVEL llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"GET_XPLEVEL|XPLEVEL="+(string)XPLEVEL,llGetOwner()); return XPLEVEL; } else { ERROR("GET_XPLEVEL ["+(string)XPLEVEL+"] AMOUNT OUT OF RANGE "+(string)MIN_XPLEVEL+"-"+(string)MAX_XPLEVEL+"! RESETTING"); } RESET(); return 0; } INCREASE_SKILL(string sname) { integer pos = llListFindList(SKILLS,[sname]); if ( pos >= 0 ) { // found skill in list integer curval = llList2Integer(SKILLS,pos + 1); if ( curval < MAXSKILL ) { SKILLS = llListReplaceList(SKILLS,[curval++],pos + 1,pos + 1); RPEVENT("increased their "+sname+" skill by one."); } llRegionSay(CHANMYRIAD,"GET_SKILL|"+sname); } else { ERROR("Requested Skill increase for nonexistent skill "+sname); } } INCREASE_STAT(string sname) { integer pos = llListFindList(STATISTICS,[sname]); if ( pos >= 0 ) { // found skill in list integer curval = llList2Integer(STATISTICS,pos + 1); if ( curval < MAXSTAT ) { STATISTICS = llListReplaceList(STATISTICS,[curval++],pos + 1,pos + 1); RPEVENT("increased their "+sname+" statistic by one."); } } else { ERROR("Requested stat increase for nonexistent stat "+sname); } } // LEVEL UP - Calculate bonuses related to new level LEVELUP(integer newlevel) { // In the Myriad system, each time a character gains a new level he is given two skill points, each of which can be used to purchase a new skill at level 1 or improve an existing skill by one level. SKILLPOOL += 2; // add two skill points per level // The character also gains an SFX point on every even-numbered level if the module being played supports SFX. if ( ( newlevel % 2 ) == 0 ) { // every even level SFXPOOL++; // add new SFX point } // One new health point may also be used to upgrade any one of the character's resilience lines, or saved in order to buy a box that would normally cost two points. HEALTHPOOL += 1; // add a point of health // Finally, the character earns one quarter of a statistic point to improve any one statistic with. if ( ( newlevel % 4 ) == 0 ) { // every 4th level STATPOOL++; // add a new stat point } } RESET() { // do any final work, then reset llResetScript(); } RPEVENT(string text) { llMessageLinked(LINK_THIS,MODULE_CHARSHEET,text,llGetOwner()); } SET_BACKGROUND(string abackground) { BACKGROUND = abackground; llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_BACKGROUND|BACKGROUND="+BACKGROUND,llGetOwner()); } SET_BOON(string boonname,integer boonrank) { // TODO how to verify boon names are valid? if ( boonrank >= MINBOON && boonrank <= MAXBOON ) { // rank valid? BOONS = [boonname,boonrank] + BOONS; // add boon to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_BOON|"+boonname+"="+(string)boonrank,llGetOwner()); } else { // invalid, report it ERROR("BOON "+boonname+" rank "+(string)boonrank+" value out of allowed range: "+(string)MINBOON+"-"+(string)MAXBOON); } } SET_CAREER(string acareer) { CAREER = acareer; llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_CAREER|CAREER="+CAREER,llGetOwner()); } SET_MORTAL_EFFECT(string effectname,integer effectrank) { // TODO how to verify effect name? if ( effectrank >= MINEFFECT && effectrank <= MAXEFFECT ) { // effect rank valid? MORTAL_EFFECTS = [effectname,effectrank] + MORTAL_EFFECTS; // add effect to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_MORTAL_EFFECT|"+effectname+"="+(string)effectrank,llGetOwner()); } else { // invalid, report it ERROR("MORTAL EFFECT "+effectname+" rank "+(string)effectrank+" value out of allowed range: "+(string)MINEFFECT+"-"+(string)MAXEFFECT); } } SET_SOCIAL_EFFECT(string effectname,integer effectrank) { // TODO how to verify effect name? if ( effectrank >= MINEFFECT && effectrank <= MAXEFFECT ) { // effect rank valid? SOCIAL_EFFECTS = [effectname,effectrank] + SOCIAL_EFFECTS; // add effect to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_SOCIAL_EFFECT|"+effectname+"="+(string)effectrank,llGetOwner()); } else { // invalid, report it ERROR("SOCIAL EFFECT "+effectname+" rank "+(string)effectrank+" value out of allowed range: "+(string)MINEFFECT+"-"+(string)MAXEFFECT); } } SET_MAGIC_EFFECT(string effectname,integer effectrank) { // TODO how to verify effect name? if ( effectrank >= MINEFFECT && effectrank <= MAXEFFECT ) { // effect rank valid? MAGIC_EFFECTS = [effectname,effectrank] + MAGIC_EFFECTS; // add effect to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_MAGIC_EFFECT|"+effectname+"="+(string)effectrank,llGetOwner()); } else { // invalid, report it ERROR("MAGIC EFFECT "+effectname+" rank "+(string)effectrank+" value out of allowed range: "+(string)MINEFFECT+"-"+(string)MAXEFFECT); } } SET_VEHICLE_EFFECT(string effectname,integer effectrank) { // TODO how to verify effect name? if ( effectrank >= MINEFFECT && effectrank <= MAXEFFECT ) { // effect rank valid? VEHICLE_EFFECTS = [effectname,effectrank] + VEHICLE_EFFECTS; // add effect to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_VEHICLE_EFFECT|"+effectname+"="+(string)effectrank,llGetOwner()); } else { // invalid, report it ERROR("VEHICLE EFFECT "+effectname+" rank "+(string)effectrank+" value out of allowed range: "+(string)MINEFFECT+"-"+(string)MAXEFFECT); } } SET_EQUIPMENT(string equipmentname,integer equipmentamount) { // TODO how to verify the equipment name is valid? if ( equipmentamount >= MINEQUIPPED && equipmentamount <= MAXEQUIPPED ) { // amount valid? EQUIPMENT = [equipmentname,equipmentamount] + EQUIPMENT; // add equipment to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_EQUIPMENT|"+equipmentname+"="+(string)equipmentamount,llGetOwner()); } else { // invalid, report it ERROR("EQUIPMENT "+equipmentname+" amount "+(string)equipmentamount+" value out of allowed range: "+(string)MINEQUIPPED+"-"+(string)MAXEQUIPPED); } } SET_ESTATE(string anestate) { ESTATE = anestate; DEBUG("Estate: ["+ESTATE+"]"); } SET_FLAW(string flawname,integer flawrank) { // TODO how to verify flaw names are valid? if ( flawrank >= MINFLAW && flawrank <= MAXFLAW ) { // rank valid? FLAWS = [flawname,flawrank] + FLAWS; // add flaw to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_FLAW|"+flawname+"="+(string)flawrank,llGetOwner()); } else { // invalid, report it ERROR("FLAW "+flawname+" rank "+(string)flawrank+" value out of allowed range: "+(string)MINFLAW+"-"+(string)MAXFLAW); } } SET_GP(integer gpamt) { if ( gpamt >= 0 ) { GP = gpamt; } else { ERROR("SET_GP("+(string)gpamt+") REQUESTED AMOUNT OUT OF RANGE: LESS THAN ZERO"); } } SET_HEALTHPOOL(integer healthamt) { if ( healthamt >= 0 ) { HEALTHPOOL = healthamt; } else { ERROR("SET_HEALTHPOOL("+(string)healthamt+") REQUESTED AMOUNT OUT OF RANGE: LESS THAN ZERO"); } } SET_NAME(string aname) { NAME = aname; llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_NAME|NAME="+NAME,llGetOwner()); } SET_PROGRESSION(string method) { method = llToUpper(method); if ( method == "LEVEL-BASED" || method == "GRADUAL" || method == "RANDOM" ) { PROGRESSION = method; DEBUG("Character progression method set to: "+method); } else { ERROR("Unknown progression method: "+method+"! Roleplay progress will not be counted."); } } SET_QUOTE(string quotename) { // TODO how to verify quote? QUOTES = [quotename] + QUOTES; // add quote to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_QUOTE|QUOTE="+quotename,llGetOwner()); } SET_RESILIENCE(string resname,integer resrank) { // TODO how to verify resilience names are valid? if ( resrank < 0 || resrank > 20 ) { ERROR("Resilience rank "+(string)resrank+" out of allowed range "+(string)MINRESILIENCE+"-"+(string)MAXRESILIENCE); return; } integer curpos = llListFindList(CURRENT_RESILIENCES,[resname]); if ( curpos >= 0 ) { CURRENT_RESILIENCES = llListReplaceList(CURRENT_RESILIENCES,[resrank],curpos + 1,curpos + 1); } else { CURRENT_RESILIENCES = [resname,resrank] + CURRENT_RESILIENCES; // add to current list too } integer maxpos = llListFindList(RESILIENCES,[resname]); if ( maxpos >=0 ) { RESILIENCES = llListReplaceList(RESILIENCES,[resrank],maxpos + 1,maxpos + 1); } else { RESILIENCES = [resname,resrank] + RESILIENCES; // add resilience to list } llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_RESILIENCE|"+resname+"="+(string)resrank,llGetOwner()); } SET_RP(integer rpamt) { if ( rpamt >= 0 ) { RP = rpamt; } else { ERROR("SET_RP("+(string)rpamt+") REQUESTED AMOUNT OUT OF RANGE: LESS THAN ZERO"); } } SET_SPECIES(string aspecies) { SPECIES = aspecies; llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_SPECIES|SPECIES="+SPECIES,llGetOwner()); } SET_SFXPOOL(integer sfxamt) { if ( sfxamt >= 0 ) { SFXPOOL = sfxamt; } else { ERROR("SET_SFXPOOL("+(string)sfxamt+") REQUESTED AMOUNT OUT OF RANGE: LESS THAN ZERO"); } } SET_SKILL(string skillname,integer skillrank) { // TODO how to verify skill names are valid? if ( skillrank >= MINSKILL && skillrank <= MAXSKILL ) { // skill rank valid? SKILLS = [skillname,skillrank] + SKILLS; // add skill to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_SKILL|"+skillname+"="+(string)skillrank,llGetOwner()); } else { // invalid, report it ERROR("SKILL "+skillname+" rank "+(string)skillrank+" value out of allowed range: "+(string)MINSKILL+"-"+(string)MAXSKILL); } } SET_SKILLPOOL(integer skillamt) { if ( skillamt >= 0 ) { SKILLPOOL = skillamt; } else { ERROR("SET_SKILLPOOL("+(string)skillamt+") REQUESTED AMOUNT OUT OF RANGE: LESS THAN ZERO"); } } SET_STAT(string statname,integer statrank) { // TODO how to verify stat names are valid? if ( statrank >= MINSTAT && statrank <= MAXSTAT ) { // rank valid? integer statpos = llListFindList(STATISTICS,[statname]); if ( statpos < 0 ) { STATISTICS = [statname,statrank] + STATISTICS; // add statistic to list } else { STATISTICS = llListReplaceList(STATISTICS,[statrank],statpos + 1,statpos + 1); } llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_STATISTIC|"+statname+"="+(string)statrank,llGetOwner()); } else { // invalid, report it ERROR("STATISTIC "+statname+" rank "+(string)statrank+" value out of allowed range: "+(string)MINSTAT+"-"+(string)MAXSTAT); } } SET_STATPOOL(integer statamt) { if ( statamt >= 0 ) { STATPOOL = statamt; } else { ERROR("SET_STATPOOL("+(string)statamt+") REQUESTED AMOUNT OUT OF RANGE: LESS THAN ZERO"); } } SET_STUNT(string stuntname) { // TODO how to verify stunt? STUNTS = [stuntname] + STUNTS; // add stunt to list llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_STUNT|STUNT="+stuntname,llGetOwner()); } SET_XP(integer xpamt) { if ( xpamt >= MINXP && xpamt <= MAXXP ) { XP = xpamt; CALCULATE_LEVEL_BY_XP(); // inform all other modules that might need to track XP llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_XP|XP="+(string)XP,llGetOwner()); } else { ERROR("SET_XP("+(string)xpamt+") REQUESTED AMOUNT OF RANGE: "+(string)MINXP+"-"+(string)MAXXP); } } SET_XPLEVEL(integer xplevelamt) { if ( xplevelamt >= MIN_XPLEVEL && xplevelamt <= MAX_XPLEVEL ) { XPLEVEL = xplevelamt; // save local state llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"SET_XPLEVEL|XPLEVEL="+(string)XPLEVEL,llGetOwner()); } else { ERROR("SET_XPLEVEL("+(string)xplevelamt+") REQUESTED AMOUNT OUT OF RANGE: "+(string)MIN_XPLEVEL+"-"+(string)MAX_XPLEVEL); } } SETUP() { FLAG_DEBUG = FALSE; PLAYERID = llGetOwner(); CHANOBJECT = (integer)("0x"+llGetSubString(llGetKey(),0,6)); if ( HANDOBJECT != 0 ) llListenRemove(HANDOBJECT); HANDOBJECT = llListen(CHANOBJECT,"",NULL_KEY,""); llRegionSay(CHANMYRIAD,"GET_REGION_SETTING|PROGRESSION"); // ask for this first, asking for estate triggers find_notecard function llRegionSay(CHANMYRIAD,"GET_REGION_SETTING|ESTATE"); MENU_TIMER = MENU_TIMEOUT; llSetTimerEvent(1.0); } SKILL_FAILED(string sname) { if ( PROGRESSION != "RANDOM" ) return; // tracking skill failure only applies to random progression - ignore message integer pos = llListFindList(SKILLS_INCREASED,[sname]); if ( pos >= 0 ) { // skill already in increase list integer current = llList2Integer(SKILLS_INCREASED,pos + 1); if ( current < 4 ) { // not yet, just note it } else { // has hit 5, increase skill and stat INCREASE_SKILL(sname); // increase skill SKILLS_INCREASED = llListReplaceList(SKILLS_INCREASED,[0],pos + 1,pos + 1); } } else { // skill not in increase list SKILLS_INCREASED = [sname,1] + SKILLS_INCREASED; // add the skill to the increased list } SKILL_INCREASES++; // now - 5 skill increases = new skill earned if ( SKILL_INCREASES == 5 ) { SKILL_INCREASES = 0; NEW_SKILLS++; RPEVENT("earned a new skill!"); } } PARSE(string message,key id) { //DEBUG("Parse: message=["+message+"] id=["+(string)id+"]"); // First - handle type 1 messages that do not require breaking down string msg = llToLower(message); if ( msg == "debugoff" ) { FLAG_DEBUG=FALSE; } // turn off debugging on request if ( msg == "debugon" ) { FLAG_DEBUG=TRUE; } // turn on debugging on request if ( msg == "reset" ) { RESET(); } // reset on request if ( msg == "version") { GETVERSION(); } // respond with version info when requested if ( msg == "get_xp" ) { GET_XP(); return; } if ( msg == "get_xplevel" ) { GET_XPLEVEL(); return; } if ( msg == "dump_sheet" ) { DUMP_SHEET(id); return; } // id is UUID of character builder, sent over from HUD script if ( msg == "dump_progress" ) { DUMP_PROGRESS(id); return; } // id is UUID of character builder, sent over from HUD script if ( msg == "del_progress" ) { DEL_PROGRESS(id); return; } // id is UUID of item that requested del progress if ( msg == "add_xp" ) { ADD_XP(id); return; } // add a point of total and spendable XP // Set up variables to process type 2 and type 3 messages list tokens; string cmd; string data; list subtokens; string attrib; integer idata; string sdata; // process type 2 bus messages COMMAND|DATA=...|DATA=... if ( llSubStringIndex(message,DIV) >=0 ) { tokens = llParseString2List(message,[DIV],[]); cmd = llToLower(llStringTrim(llList2String(tokens,0),STRING_TRIM)); data = llList2String(tokens,1); subtokens = llParseString2List(data,["="],[]); attrib = llList2String(subtokens,0); idata = llList2Integer(subtokens,1); sdata = llList2String(subtokens,1); //if ( command == "SET_ESTATE" ) { SET_ESTATE(svalue); return;} if ( cmd == "set_xp" ) { SET_XP(idata); return; } if ( cmd == "set_xplevel" ) { SET_XPLEVEL(idata); return; } if ( cmd == "get_stat" ) { GET_STAT(attrib); return; } if ( cmd == "get_resilience" ) { GET_RESILIENCE(attrib); return; } if ( cmd == "get_maxresilience" ) { GET_MAXRESILIENCE(attrib); return; } if ( cmd == "set_resilience" ) { SET_RESILIENCE(attrib,idata); return; } // Progress if ( cmd == "skill_failed" ) { SKILL_FAILED(sdata); return; } // SKILL_FAILED|SKILL=name if ( cmd == "progress" ) { if ( llToLower(attrib) == "xp" ) { XP = idata; if ( XP > MAXXP ) XP = MAXXP; llSay(PUBLIC_CHANNEL,"Progress XP: "+(string)XP); return; } if ( llToLower(attrib) == "xplevel" ) { XPLEVEL = idata; if ( XPLEVEL > MAX_XPLEVEL ) XPLEVEL = MAX_XPLEVEL; llSay(PUBLIC_CHANNEL,"Progress XP Level: "+(string)XPLEVEL); return; } if ( llToLower(attrib) == "statpool" ) { STATPOOL=idata; llSay(PUBLIC_CHANNEL,"Progress Statistics Pool: "+(string)STATPOOL); return; } if ( llToLower(attrib) == "healthpool" ) { HEALTHPOOL=idata; llSay(PUBLIC_CHANNEL,"Progress Resilience Pool: "+(string)HEALTHPOOL); return; } if ( llToLower(attrib) == "skillpool" ) { SKILLPOOL=idata; llSay(PUBLIC_CHANNEL,"Progress Skill Pool: "+(string)SKILLPOOL); return; } if ( llToLower(attrib) == "sfxpool" ) { SFXPOOL=idata; llSay(PUBLIC_CHANNEL,"Progress SFX Pool: "+(string)SFXPOOL); return; } return; } // done processing commands with DIV return; } // Process type 3 messages CATEGORY=ATTRIBUTE,VALUE tokens = llParseString2List(message,["="],[]); cmd = llToLower(llStringTrim(llList2String(tokens,0),STRING_TRIM)); data = llList2String(tokens,1); subtokens = llCSV2List(data); attrib = llList2String(subtokens,0); idata = llList2Integer(subtokens,1); sdata = llList2String(subtokens,1); if ( cmd == "version" ) { CHECK_CARDVERSION(data); return;} if ( cmd == "name" ) { SET_NAME(data); return; } if ( cmd == "species" ) { SET_SPECIES(data); return;} if ( cmd == "background" ) { SET_BACKGROUND(data); return; } if ( cmd == "career" ) { SET_CAREER(data); return; } if ( cmd == "statistic" ) { SET_STAT(attrib,idata); return; } if ( cmd == "resilience" ) { SET_RESILIENCE(attrib,idata); return;} if ( cmd == "boon" ) { SET_BOON(attrib,idata); return;} if ( cmd == "flaw" ) { SET_FLAW(attrib,idata); return;} if ( cmd == "skill" ) { SET_SKILL(attrib,idata); return;} if ( cmd == "mortal_effect" ) { SET_MORTAL_EFFECT(attrib,idata); return;} if ( cmd == "social_effect" ) { SET_SOCIAL_EFFECT(attrib,idata); return;} if ( cmd == "magic_effect" ) { SET_MAGIC_EFFECT(attrib,idata); return;} if ( cmd == "vehicle_effect" ) { SET_VEHICLE_EFFECT(attrib,idata); return;} if ( cmd == "stunt" ) { SET_STUNT(data); return;} if ( cmd == "quote" ) { SET_QUOTE(data); return;} if ( cmd == "equipment" ) { SET_EQUIPMENT(attrib,idata); return;} if ( cmd == "xp" ) { SET_XP(idata); return; } if ( cmd == "xplevel" ) { SET_XPLEVEL(idata); return; } if ( cmd == "gp" ) { SET_GP(idata); return; } if ( cmd == "statpool" ) { SET_STATPOOL(idata); return; } if ( cmd == "healthpool" ) { SET_HEALTHPOOL(idata); return; } if ( cmd == "skillpool" ) { SET_SKILLPOOL(idata); return; } if ( cmd == "sfxpool" ) { SET_SFXPOOL(idata); return; } if ( cmd == "rp" ) { SET_RP(idata); return; } if ( cmd == "character_loaded" ) { llSay(PUBLIC_CHANNEL,llKey2Name(PLAYERID)+"'s character loaded."); } } default { // dataserver called for each line of notecard requested - process character sheet dataserver(key queryid,string data) { if ( queryid == QUERY ) { // ataserver gave us line we asked for? if ( data != EOF ) { // we're not at end of notecard file? if ( llGetSubString(data,0,0) == "#" ) { // does this line start with comment mark? QUERY = llGetNotecardLine(CARD,LINE++); // ignore comment and ask for the next line return; } PARSE(data,llGetOwner()); // parse incoming notecard line QUERY = llGetNotecardLine(CARD,LINE++); // finished with known keywords, get next line } else { // end of notecard // TODO how to verify entire character sheet was completed and loaded? llMessageLinked(LINK_THIS,MODULE_CHARSHEET,"CHARACTER_LOADED",llGetOwner()); // done loading DEBUG("Character Sheet Loaded."); } // end if data not equal eof } // end if query id equal } // end if data server event link_message(integer sender_num,integer num,string str,key id) { if ( num == MODULE_CHARSHEET || num == LM_SENDTOATTACHMENT ) return; // ignore link messages not sent to us specifically DEBUG("EVENT: link_message("+(string)sender_num+","+(string)num+","+str+","+(string)id+")"); PARSE(str,id); // parse incoming message } listen(integer channel,string name,key id, string msg) { name = ""; // LSLint id = NULL_KEY; // LSLint if ( channel == CHANOBJECT ) { list tokens = llParseString2List(msg,[DIV],[]); string command = llToLower(llStringTrim(llList2String(tokens,0),STRING_TRIM)); if ( command == "region_setting" ) { list sublist = llParseString2List(llList2String(tokens,1),["="],[]); if ( llToLower(llStringTrim(llList2String(sublist,0),STRING_TRIM)) == "progression" ) { SET_PROGRESSION(llToUpper(llList2String(sublist,1))); return; } if ( llToLower(llStringTrim(llList2String(sublist,0),STRING_TRIM)) == "estate" ) { SET_ESTATE(llList2String(sublist,1)); FIND_NOTECARD(); return; } return; } if ( command == "skill" ) { integer t = llGetListLength(tokens); integer c; for ( c = 0; c < t; c++) { list sublist = llParseString2List(llList2String(tokens,c),["="],[]); string attrib = llToLower(llList2String(sublist,0)); if ( attrib == "basestat" ) { string value = llList2String(sublist,1); INCREASE_STAT(value); } } } PARSE(msg,id); // parse incoming chat message not REGION_SETTING or SKILL data return; } if ( channel == MENU_CHANNEL ) { if ( msg == "Default" ) { CARD = DEFAULT; } else { integer listpos = llListFindList(CHARACTERS,[msg]); if ( listpos >= 0 ) { CARD = llList2String(CHARACTERS,listpos + 1); } } DEBUG("Loading character sheet: "+CARD); llSetTimerEvent(0.0); MENU_TIMER = 0; llListenRemove(MENU_HANDLE); QUERY = llGetNotecardLine(CARD,LINE++); // ask for line from notecard and advance to next line return; } } state_entry() { SETUP(); } timer() { MENU_TIMER--; // timer still running, decrement if ( MENU_TIMER <= 0 ) { // timed out DEBUG("Character Sheet Menu timed out. Using default character sheet."); // tell the owner llListenRemove(MENU_HANDLE); // remove the listener MENU_TIMER = 0; llSetTimerEvent(0.0); // stop the timer CARD = DEFAULT; QUERY = llGetNotecardLine(CARD,LINE++); } } } // END