Myriad_Lite_Module_Armor-v0.0.1-20120212.lsl
// Myriad_Lite_Module_Armor-v0.0.1-20120212.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.
string VERSION = "0.0.1";
string VERDATE = "20120212";
integer FLAG_DEBUG; // configure in setup
key PLAYERID = NULL_KEY; // cached player UUID
integer MINATTACH = 1; // min valid attach point
integer MAXWEAR = 30; // max valid in-world wearable attach point
integer MINARMOR = 1; // min armor defense value
integer MAXARMOR = 5; // max armor defense value
integer CURARMOR = 0; // highest armor value worn out of all armor worn, not a total
list ARMOR = [];
list ARMORPOWER = [];
integer POWERARMOR;
integer BATTERY;
integer MAXBATTERY;
integer MAXEFFECTTIME = 3; // maximum time to show armor hit/blocked effects
integer EFFECTTIME; // how much time is left to show armor effects
integer ARMOR_ON; // is armor "on" and protecting?
// string names for each attach point - waste of memory?
list ATTACHPOINTS = ["INVALID","chest","head","left shoulder","right shoulder","left hand","right hand","left foot","right foot","back","pelvis","mouth","chin","left ear","right ear","left eye","right eye","nose","right upper arm","right lower arm","left upper arm","left lower arm","right hip","right upper leg","right lower leg","left hip","left upper leg","left lower leg","stomach","left pectoral","right pectoral","HUD Center 2","HUD Top Right","HUD Top","HUD Top Left","HUD Center","HUD Bottom Left","HUD Bottom","HUD Bottom Right" ];
integer LM_SENDTOATTACHMENT = 0x80000000;
string DIV = "|"; // message divider
//============================================================================
// SETUP - begin
//============================================================================
SETUP() {
FLAG_DEBUG= FALSE; // do we want debug messages
ARMOR_ON = FALSE; // is armor "on" and protecting? off by default to save battery
BATTERY = 3600; // start with charged battery FIXME how to save state from wear to wear
MAXBATTERY = 3600; // total battery capacity when fully charged - in seconds
PLAYERID = llGetOwner(); // remember the owner's UUID
integer attachpoints = MAXWEAR; // counting from 0 to 30
while ( attachpoints-- ) {
ARMOR = ARMOR + [0]; // create 30 empty armor slots - avoids SL stack depth error and LSLINT warning
ARMORPOWER = ARMORPOWER + [FALSE]; // create 30 empty power armor flags - avoid SL stack depth and LSLINT warning
}
llSetTimerEvent(0.0); // stop any running timer
}
//============================================================================
// RESET - shut down running animations then reset the script to reload character sheet
//============================================================================
RESET() {
llResetScript(); // now reset
}
//============================================================================
// DEBUG - show errors on debug channel with wearer name for sorting
//============================================================================
DEBUG(string dmessage) {
if ( FLAG_DEBUG == TRUE ) llSay(DEBUG_CHANNEL,"MODULE_ARMOR_DEBUG: ("+llKey2Name(PLAYERID)+"): "+dmessage);
}
//============================================================================
// ERROR - show errors on debug channel with wearer name for sorting
//============================================================================
ERROR(string emessage) {
llSay(DEBUG_CHANNEL,"ERROR ("+llKey2Name(PLAYERID)+"): "+emessage);
}
//============================================================================
// GETVERSION
//============================================================================
GETVERSION() {
SENDTOHUD("VERSION="+VERSION+DIV+"VERSIONDATE="+VERDATE+DIV+llGetObjectName());
}
//============================================================================
// SENDTOHUD - send reponses to HUD as Link Messages
//============================================================================
SENDTOHUD(string str) {
DEBUG("SENDTOHUD("+str+")");
llMessageLinked(LINK_ALL_OTHERS,LM_SENDTOATTACHMENT,str,PLAYERID);
}
//============================================================================
// WEARARMOR - Wearing a piece of armor
//============================================================================
WEARARMOR(integer waattachpoint,integer waamount,integer wapower,string waname) {
DEBUG("ATTACH Attachpoint=["+(string)waattachpoint+"] Amount=["+(string)waamount+"] Power=["+(string)wapower+"] Name=["+waname+"]");
if ( waattachpoint < MINATTACH || waattachpoint > MAXWEAR ) { // valid attach point?
ERROR("Invalid armor attachment point "+llList2String(ATTACHPOINTS,waattachpoint));
return;
}
if ( waamount < MINARMOR || waamount > MAXARMOR ) { // is armor rating valid or legal?
ERROR("Invalid armor amount "+(string)waamount+" out of range "+(string)MINARMOR+"-"+(string)MAXARMOR);
return;
}
if ( wapower != TRUE && wapower != FALSE ) {
ERROR("Cannot determine if worn armor is power armor.");
return;
}
// FIXME move ARMOR and POWERARMOR into single list?
ARMORPOWER = llListReplaceList(ARMORPOWER,[wapower],waattachpoint,waattachpoint); // insert armor value into armor list
// FIXME move ARMOR to 3-element strided list? [attachpoint,value,name?]
ARMOR = llListReplaceList(ARMOR,[waamount],waattachpoint,waattachpoint); // insert armor value into armor list
llOwnerSay("Armor "+waname+" ("+(string)waamount+") attached to "+llList2String(ATTACHPOINTS,waattachpoint));
RECALCULATE_ARMOR(); // find new highest armor value
}
//============================================================================
// REMOVEARMOR - Removing a piece of armor
//============================================================================
REMOVEARMOR(integer raattachpoint,integer raamount,integer rapower,string raname) {
DEBUG("DETACH Attachpoint=["+(string)raattachpoint+"] Amount=["+(string)raamount+"] Power=["+(string)rapower+"] Name=["+raname+"]");
if ( raattachpoint < MINATTACH || raattachpoint > MAXWEAR ) { // valid attach point?
ERROR("Invalid armor detachment point "+llList2String(ATTACHPOINTS,raattachpoint));
return;
}
if ( raamount < MINARMOR || raamount > MAXARMOR ) { // is armor rating valid or legal?
ERROR("Invalid armor amount "+(string)raamount+" out of range "+(string)MINARMOR+"-"+(string)MAXARMOR);
return;
}
if ( rapower != TRUE && rapower != FALSE ) {
ERROR("Cannot determine if detached armor is power armor.");
return;
}
// FIXME move ARMOR and POWERARMOR into single list?
ARMORPOWER = llListReplaceList(ARMORPOWER,[FALSE],raattachpoint,raattachpoint); // insert armor value into armor list
// FIXME move ARMOR to 3-element strided list? [attachpoint,value,name?]
ARMOR = llListReplaceList(ARMOR,[0],raattachpoint,raattachpoint); // zero out the armor value in armor list
llOwnerSay("Armor "+raname+" ("+(string)raamount+") detached from "+llList2String(ATTACHPOINTS,raattachpoint));
RECALCULATE_ARMOR(); // find new highest armor value
}
//============================================================================
// RECALCULATE_ARMOR - sets CURARMOR to highest armor value worn after attach or detach
//============================================================================
RECALCULATE_ARMOR() {
CURARMOR = 0; // start with zero armor
POWERARMOR = FALSE;
integer racount = llGetListLength(ARMOR); // how long is armor list?
while (racount--) { // look at each list item from last to first
integer rapoints = llList2Integer(ARMOR,racount); // what is armor value at this point in list?
integer pa = llList2Integer(ARMORPOWER,racount);
if ( pa == FALSE ) { // not power armor in this slot, so do check regardless of power state
if ( rapoints > CURARMOR ) { // is this armor value higher than current max?
CURARMOR = rapoints; // yes, save new highest amount
}
} else { // this is power armor in this slot.
POWERARMOR = TRUE;
if ( ARMOR_ON == TRUE && BATTERY > 0 && rapoints > CURARMOR ) {
CURARMOR = rapoints; // yes armor is on, has power, and current value is higher, save new highest amount
}
}
}
if ( POWERARMOR == TRUE ) {
llOwnerSay("Power Armor Rating is "+(string)CURARMOR);
} else {
llOwnerSay("Non-Power Armor Rating is "+(string)CURARMOR);
}
SENDTOHUD("ARMORCURRENT|"+(string)CURARMOR);
// FIXME tell world too?
}
//============================================================================
// ARMOR ON
//============================================================================
ARMORON() {
//llWhisper(CHANATTACH,"ARMORON"); // tell attachment to do some inworld special effect if needed
if ( BATTERY <= 0 && POWERARMOR == TRUE ) {
llOwnerSay("Power armor out of power. Recharge.");
return;
}
if ( POWERARMOR == TRUE ) {
llOwnerSay("Power armor activating.");
ARMOR_ON = TRUE;
SENDTOHUD("ARMORON");
llSetTimerEvent(1.0); // run a battery drain timer, battery already checked > 0
}
RECALCULATE_ARMOR();
}
//============================================================================
// ARMOROFF
//============================================================================
ARMOROFF() {
//llWhisper(CHANATTACH,"ARMOROFF"); // tell attachments to do some inworld special effects if needed
if ( POWERARMOR == TRUE ) {
llOwnerSay("Power armor deactivated.");
ARMOR_ON = FALSE;
SENDTOHUD("ARMOROFF");
llSetTimerEvent(0.0); // stop battery drain timer - FIXME does this kill effect timers?
return;
}
RECALCULATE_ARMOR();
}
//============================================================================
// CHECKBATTERY
//============================================================================
CHECKBATTERY() {
//llWhisper(CHANATTACH,"ARMORBATTERY");
if ( POWERARMOR != TRUE ) {
llOwnerSay("Cannot check battery level for non-powered armor.");
return;
}
llOwnerSay("Armor battery level: "+(string)BATTERY+" of "+(string)MAXBATTERY+" total.");
SENDTOHUD("ARMORBATTERY");
}
//============================================================================
// RECHARGE
//============================================================================
RECHARGE() {
//llWhisper(CHANATTACH,"ARMORRECHARGE");
if ( POWERARMOR != TRUE ) {
llOwnerSay("Cannot recharge non-powered armor.");
return;
}
// TODO Partial Recharges?
BATTERY = MAXBATTERY;
llOwnerSay("Armor recharged.");
SENDTOHUD("ARMORRECHARGE");
}
//============================================================================
// EFFECTHIT() - SHOW SPECIAL ARMOR EFFECTS WHEN ARMOR HIT BUT FAILS TO BLOCK
//============================================================================
EFFECTHIT() {
//llWhisper(CHANATTACH,"ARMOREFFECTHIT");
SENDTOHUD("ARMOREFFECTHIT");
EFFECTTIME = MAXEFFECTTIME; // load the countdown
llSetTimerEvent(1.0); // start the effect timer
}
//============================================================================
// EFFECTBLOCKED - CHANGE ARMOR EFFECT WHEN ARMOR HIT AND BLOCKS DAMAGE
//============================================================================
EFFECTBLOCKED() {
// your commands go here for armor special effect when armor BLOCKS a hit
// llWhisper(CHANATTACH,"ARMOREFFECTBLOCKED");
SENDTOHUD("ARMOREFFECTBLOCKED");
EFFECTTIME = MAXEFFECTTIME; // load the countdown
llSetTimerEvent(1.0); // start the effect timer
}
//============================================================================
// EFFECTOFF - RESET ARMOR TO NORMAL VIEW
//============================================================================
EFFECTOFF() {
//llWhisper(CHANATTACH,"ARMOREFFECTOFF");
SENDTOHUD("ARMOREFFECTOFF");
}
//============================================================================
// COMMAND processor
//============================================================================
COMMAND(string message) {
list fields = llParseString2List(message,[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
// General Myriad Module Commnads
if ( command == "reset" ) { RESET(); return;} // reset when told
if ( command == "version" ) { GETVERSION(); return;} // get version when needed
if ( command == "debugon" ) { FLAG_DEBUG = TRUE; return;} // enable debugging
if ( command == "debugoff" ) { FLAG_DEBUG = FALSE; return;} // disable debugging
// Armor-Specific Module Commands
if ( command == "armorattach" ) { // player attached armor somewhere
integer armorrating = llList2Integer(fields,1); // get armor value
integer armorpower = llList2Integer(fields,2); // get power armor or not
integer attachpoint = llList2Integer(fields,3); // get armor location
string armorname = llList2String(fields,4); // get armor's name
WEARARMOR(attachpoint,armorrating,armorpower,armorname); // add armor to set of armor worn
return;
}
if ( command == "armordetach" ) { // player attached armor somewhere
integer armorrating = llList2Integer(fields,1); // get armor value
integer armorpower = llList2Integer(fields,2); // get power armor or not
integer attachpoint = llList2Integer(fields,3); // get armor location
string armorname = llList2String(fields,4); // get armor's name
REMOVEARMOR(attachpoint,armorrating,armorpower,armorname); // detach armor from set of armor worn
return;
}
// armorcurrent skipped intentionally
if ( command == "armorreset" ) { RESET(); return;} // reset HUD
if ( command == "armoron" ) { ARMORON(); return;} // turn on power armor
if ( command == "armoroff" ) { ARMOROFF(); return;} // turn off power armor
if ( command == "armorbattery") { CHECKBATTERY(); return;} // check power armor battery
if ( command == "armorrecharge" ) { RECHARGE(); return;} // recharge power armor battery
if ( command == "armorcheck" ) { RECALCULATE_ARMOR(); return; } // check our current armor value
if ( command == "armoreffecthit" ) { EFFECTHIT(); return;} // show SFX on hit
if ( command == "armoreffectblocked" ) { EFFECTBLOCKED(); return;} // show SFX on hit
if ( command == "armoreffectoff" ) { EFFECTOFF(); return;} // show SFX on hit
}
//============================================================================
// DEFAULT STATE
//============================================================================
default {
//------------------------------------------------------------------------
// STATE_ENTRY - the script starts here
//------------------------------------------------------------------------
state_entry() {
SETUP(); // setup defaults and go into event wait
}
//------------------------------------------------------------------------
// ON REZ - RESET to force through state entry
//------------------------------------------------------------------------
on_rez(integer params) {
params = 0; // LSLINT
RESET();
}
//------------------------------------------------------------------------
// ON ATTACH - RESET to force through state entry
//------------------------------------------------------------------------
attach(key id) {
id = NULL_KEY; // LSLINT
RESET();
}
//------------------------------------------------------------------------
// 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?
RESET();
}
if ( changes & CHANGED_REGION || changes & CHANGED_TELEPORT ) {
RESET();
}
}
//------------------------------------------------------------------------
// INCOMING LINK MESSSAGES
//------------------------------------------------------------------------
link_message(integer sender_num,integer num,string str,key id) {
DEBUG("MOD_ARMOR: Link_Message IN "+str);
sender_num = 0; // LSLINT
num = 0; // LSLINT
id = NULL_KEY; // LSLINT
if ( llGetSubString(llStringTrim(llToLower(str),STRING_TRIM),0,4) == "armor" ) { // if armor command
COMMAND(str); // jump to command processor
return; // return here too in case we add later commands
}
}
//------------------------------------------------------------------------
// TIMER CALLED TO TURN OFF THE SPECIAL EFFECTS AND DRAIN BATTERIES
//------------------------------------------------------------------------
timer() {
if ( EFFECTTIME > 0 ) {
EFFECTTIME--;
if ( EFFECTTIME == 0 ) {
EFFECTOFF(); // turn off special effects
} // timer expired, turn off effect
}
if ( EFFECTTIME < 0 ) { // we should never have this happen, but just in case, catch and cleanup.
EFFECTTIME = 0;
EFFECTOFF();
}
if ( POWERARMOR == TRUE && ARMOR_ON == TRUE && BATTERY > 0 ) {
BATTERY--; // remove some battery
if ( BATTERY == 0 ) {
llOwnerSay("Armor battery drained. Shutting down.");
ARMOROFF(); // turn off armor
}
}
if ( BATTERY < 0 ) { // we should never have this happen, but just in case, catch and cleanup
BATTERY = 0;
ARMOROFF();
}
if ( EFFECTTIME <= 0 && ARMOR_ON == FALSE ) llSetTimerEvent(0.0); // all timers done, stop timer events
} // end timer
}