Myriad Armour

Place this in each item of armour that you wear

//============================================================================
// Myriad Lite Armor v0.0.4 20110904
// Copyright (c) 2011 By Allen Kerensky (OSG/SL)
// The Myriad RPG System was designed, written, and illustrated by Ashok Desai
// Myriad is published under a:
// Creative Commons License (Attribution 2.0 UK: England and Wales)
// Myriad Lite Armor is published under a:
// Creative Commons License Attribution-NonCommercial-ShareAlike 3.0 Unported
//============================================================================

//============================================================================
// MESSAGE FORMATS
//============================================================================
// CHANATTACH - IN - REGISTERATTACHMENTS
// CHANATTACH - OUT - ATTACHARMOR|int ARMORRATING|int ATTACHPOINT|string OBJECTNAME
// CHANATTACH - OUT - DETACHARMOR|int ARMORRATING|int ATTACHPOINT|string OBJECTNAME
// CHANATTACH - IN - ARMORHIT
// CHANATTACH - IN - ARMORBLOCKED

//============================================================================
// CONSTANTS - variables which don't change over runtime
//============================================================================
// Example Armor (Myriad PDF p64, Myriad Special Edition p98)
// Archaic Armor Ratings
// 1 Soft leather, heavy cloth
// 2 Hardened leather, thick hide
// 3 Chain mail, dragon hide
// 4 Full plate mail, mithril chain
// 5 Mithril plate mail
// Modern Armor Ratings
// 1 Leather jacket
// 2 Bullet-proof vest
// 3 SWAT tactical armor
// 4 Advanced military armor
// 5 Sci-fi powered battle armor
integer ARMORRATING = 1; // the *actual* amount of protection THIS piece of armor provides
integer MINARMOR = 1; // the minimum amount of protection a piece of armor can offer, checked by HUD
integer MAXARMOR = 5; // the maximum amount of protection a piece of armor can offer, checked by HUD
string DIV="|"; // the divider used between fields on a Myriad Lite messages
integer MINATTACHPOINT = 1; // minimum allowed attachment point number
integer MAXATTACHPOINT = 30; // maximum allowed avatar/inworld attachpoint number for multiattach/HUD attach cheaters
integer MAXEFFECTTIME = 3; // maximum time to show armor hit/blocked effects
integer POWERARMOR = FALSE; // does this armor burn power?
integer MAXBATTERY = 3600; // how many seconds can armor run before recharge?

//============================================================================
// RUNTIME VARIABLES - variables which should change during runtime
//============================================================================
key OWNER = NULL_KEY; // holds UUID of owner
key WEARER = NULL_KEY; // holds UUID of last wearer, so we can send detach message to correct meter
integer ATTACHPOINT = 0; // the avatar position where armor attached to or detached from
integer CHANOWNER; // the chat channel the owner's ML HUD should be listening on
integer HANDOWNER; // a chat channel handle to remove the listener with later
integer CHANATTACH; // owner's attachment channel
integer HANDATTACH; // chat handle for attachment channel
integer FLAG_DEBUG = FALSE; // show debug messages or not?
integer ARMOR_ON = TRUE; // is armor "on" and protecting?
integer BATTERY; // current charge left for power armor
integer EFFECTTIME; // how much time is left to show armor effects

//============================================================================
// DEBUG - show debug chat with wearer name for sorting
//============================================================================
DEBUG(string dmessage) {
    if ( FLAG_DEBUG == TRUE ) { // debugging?
        llSay(DEBUG_CHANNEL,"DEBUG ("+llKey2Name(llGetOwner())+"): "+dmessage);
    }
}

//============================================================================
// ERROR - show errors on debug channel with wearer name for sorting
//============================================================================
ERROR(string emessage) {
    llSay(DEBUG_CHANNEL,"ERROR ("+llKey2Name(llGetOwner())+"): "+emessage);
}

//============================================================================
// EFFECTHIT() - SHOW SPECIAL ARMOR EFFECTS WHEN ARMOR HIT BUT FAILS TO BLOCK
//============================================================================
EFFECTHIT() {
    // your commands go here for armor special effect when armor hit and does not block

    // end of special effects
    EFFECTTIME = MAXEFFECTTIME; // load the countdown
}

//============================================================================
// EFFECTBLOCKED - CHANGE ARMOR EFFECT WHEN ARMOR HIT AND BLOCKS DAMAGE
//============================================================================
EFFECTBLOCKED() {
    // your commands go here for armor special effect when armor BLOCKS a hit

    // end of special effects
    EFFECTTIME = MAXEFFECTTIME; // load the countdown
}

//============================================================================
// EFFECTOFF - RESET ARMOR TO NORMAL VIEW
//============================================================================
EFFECTOFF() {
    // your commands go here to turn off armor effects

    // end of armor reset
}

//============================================================================
// ARMOR ON - ACTIVATE POWERED ARMOR
//============================================================================
ARMORON() {
    if ( POWERARMOR == TRUE && BATTERY < 0 ) {
        llOwnerSay("Armor out of power. Recharge.");
        return;
    }
    ARMOR_ON = TRUE;
    // your code here
    llSetLinkAlpha(LINK_SET,1.0,ALL_SIDES);
    if ( POWERARMOR == TRUE ) llSetTimerEvent(1.0); // run a battery drain timer
}

//============================================================================
// ARMOR OFF - DEACTIVATE POWERED ARMOR
//============================================================================
ARMOROFF() {
    ARMOR_ON = FALSE;
    llOwnerSay("Power armor shutting down.");
    // your code here
    llSetLinkAlpha(LINK_SET,0.0,ALL_SIDES);
}

//============================================================================
// CHECK BATTERY
//============================================================================
CHECKBATTERY() {
    llOwnerSay("Armor battery level: "+(string)BATTERY+" of "+(string)MAXBATTERY+" total.");
}

//============================================================================
// RECHARGE POWER ARMOR BATTERY
//============================================================================
RECHARGE() {
    // TODO Partial Recharges?
    BATTERY = MAXBATTERY;
    llOwnerSay("Armor recharged.");
}

//============================================================================
// GLOBAL SETUP()
//============================================================================
SETUP() {
    // calculate a dynamic chat channel based on owner key, for where the
    // wearer's ML HUD should be listening of attachment -specific events
    CHANATTACH = (integer)("0x"+llGetSubString((string)llGetOwner(),1,7));
    // open a channel, listening on player HUD channel, save handle for later close if needed
    HANDATTACH = llListen(CHANATTACH,"",NULL_KEY,"");
    if ( POWERARMOR == TRUE ) BATTERY = MAXBATTERY; // charge the battery to full
    llSetLinkAlpha(LINK_SET,1.0,ALL_SIDES); // show the armor
}

//============================================================================
// DEFAULT
//============================================================================
default {
    //------------------------------------------------------------------------
    // DEFAULT STATE ENTRY - begin our setup or call a setup block
    //------------------------------------------------------------------------
    state_entry() {
        SETUP(); // call the event-independent SETUP code
    }
    //------------------------------------------------------------------------
    // DEFAULT ON_REZ
    //------------------------------------------------------------------------
    on_rez(integer start_param) {
        SETUP(); // call event independent SETUP code        
    }   
    //------------------------------------------------------------------------
    // DEFAULT ATTACH - Called for detach too
    //------------------------------------------------------------------------
    attach(key id) {
        SETUP(); // call event-independent SETUP code
        // is this an attach event or detach event?
        if ( id != NULL_KEY ) { // a valid key means its an attach
            WEARER = id; // save who attached this armor piece for use during detach
            ATTACHPOINT = llGetAttached(); // where was this armor attached?
            // this should NOT be necessary, since it should match CHANPLAYER
            // however, armor can be dropped! So, wearer may NOT be owner. Need changed block?
            integer dynchan = (integer)("0x"+llGetSubString((string)WEARER,1,7));
            // Send ATTACHARMOR message to WEARER HUD
            llWhisper(dynchan,"ATTACHARMOR"+DIV+(string)ARMORRATING+DIV+(string)ATTACHPOINT+DIV+llGetObjectName());
        } else { // else the id equals NULL_KEY which happens for detach
            // calculate dynamic channel for person last wearing armor piece
            integer dynchan = (integer)("0x"+llGetSubString((string)WEARER,1,7));
            if ( dynchan != 0 ) { // did the dynamic channel check give us usable channel number <0 or >0 but not actually 0?
                // Send DETACHARMOR message to previous wearer's HUD
                llWhisper(dynchan,"DETACHARMOR"+DIV+(string)ARMORRATING+DIV+(string)ATTACHPOINT+DIV+llGetObjectName());
            } else { // the dynamic channel was 0?
                // how did we get in this mess? a detach without an attach. Report error. Design failure somewhere. THIS SHOULD NEVER HAPPEN.
                DEBUG("DETACH EVENT WITHOUT PREVIOUS ATTACH?");
            } // end of if dynchan not equal zero
            WEARER = NULL_KEY; // armor detached and reported as such, so we can forget previous wearer
            ATTACHPOINT = 0; // armor detached and reported as such, so we can forget previous attach point
        } // end of if id not equal null key
    }
    //------------------------------------------------------------------------
    // DEFAULT CHANGED
    //------------------------------------------------------------------------
    changed(integer change) {
        if ( change & CHANGED_OWNER ) { // if armor has a new owner from take-from-ground or copy, resetup
            SETUP(); // call event-independent setup code, in this case to update channels
        }
    }    
    //------------------------------------------------------------------------
    // DEFAULT LISTEN
    //------------------------------------------------------------------------
    listen(integer channel,string speakername,key speakerid,string message) {
        ATTACHPOINT = llGetAttached(); // get location we're attached to
        if ( channel == CHANATTACH ) { // is this message on the attach channel?
            if ( message == "ARMORHIT" ) { // is this an armor hit?
                EFFECTHIT(); // turn on armor hit effects
                return; // message processed, exit early
            }
            if ( message == "ARMORBLOCKED" ) { // did the armor block the hit too
                EFFECTBLOCKED(); // turn on armor block effects
                return; // message processed, exit early
            }
            // ML HUD sent a request for any attached items
            if ( ( message == "REGISTERATTACHMENTS" ) && ( ATTACHPOINT >= MINATTACHPOINT ) && ( ATTACHPOINT <= MAXATTACHPOINT ) ) {
                WEARER = llGetOwner(); // get armor owner BUG: what if owner not equal wearer?
                // calculate the dynamic channel of the wearer
                integer dynchan = (integer)("0x"+llGetSubString((string)WEARER,1,7));
                // send the ATTACHARMOR to ML HUD to register that this armor piece is worn
                llWhisper(dynchan,"ATTACHARMOR"+DIV+(string)ARMORRATING+DIV+(string)ATTACHPOINT+DIV+llGetObjectName());
                return; // message processed, exit early
            } // end of if message equal REGISTERATTACHMENTS            
            if ( message == "ARMORON" ) { ARMORON(); return;} // activate power armor
            if ( message == "ARMOROFF" ) { ARMOROFF(); return;} // deactivate power armor
            if ( message == "RECHARGE" ) { RECHARGE(); return;} // recharge power armor battery?
            if ( message == "CHECKBATTERY" ) { CHECKBATTERY(); return;} // check battery level
        } // end if channel CHANATTACH
    }    
    //------------------------------------------------------------------------
    // TIMER CALLED TO TURN OFF THE SPECIAL EFFECTS
    //------------------------------------------------------------------------
    timer() {
        EFFECTTIME--;
        if ( EFFECTTIME <= 0 ) { 
            EFFECTOFF(); // turn off special effects
            EFFECTTIME=0; // make it zero anyway
        } // timer expired, turn off effect
        if ( POWERARMOR == TRUE && ARMOR_ON == TRUE ) {
            BATTERY--; // remove some battery
            if ( BATTERY <= 0 ) {
                llOwnerSay("Armor battery drained. Shutting down.");
                ARMOROFF(); // turn off armor
            }
        }
        if ( EFFECTTIME <= 0 || ARMOR_ON == FALSE ) llSetTimerEvent(0.0); // all timers done, stop timer events
    } // end timer
} // end default
//============================================================================
// END
//============================================================================
Creative Commons License
This work by