Myriad Firearm (Including Bows)
updated February 11 2012

Guns, bows, spears, anything involving missile type weapons

//============================================================================
// Myriad Lite Firearm v1.4 20110903
// 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 Firearm is published under a:
// Creative Commons License Attribution-NonCommercial-ShareAlike 3.0 Unported
//----------------------------------------------------------------------------
// Based on phillip linden (SL) gunscript
// With many refinements by Rhonin Nissondorf (SL)
// Retrieved 2011-04-30 from http://wiki.secondlife.com/wiki/Gun_Script
// Copyright (c) 2009 Linden Research, Inc.
// Licensed under Creative Commons Attribution-Share Alike 3.0 (CC-BY-SA 3.0)
// Converted to OSSL by Allen Kerensky (SL/OSG).
//===========================================================================

//===========================================================================
// MESSAGE FORMAT REFERENCE
//===========================================================================
// CHANMYRIAD OUT - RPEVENT|str event_message
// CHANATTACH IN - REGISTERATTACHMENTS
// CHANATTACH OUT - ATTACHRANGED|int damagedice|int attachpoint|str attachmentname
// CHANATTACH OUT - DETACHRANGED|int damagedice|int attachpoint|str attachmentname

//===========================================================================
// GLOBAL VARIABLES - variables to be changed  below.
//===========================================================================
float BULLET_VELOCITY = 30.0;                       // change this to change the speed of the bullet.
float REPEAT_DELAY = 0.20;                          // delay between bullets, i recommend you dont' set it to  low.
string gunsound = "pistol_shot.wav";                            // string; name of sound in inventory
string ammo =  "Myriad Lite Bullet v0.0.3 20110813"; //name of desired object to be shot out. Must be in the inventory of the "gun".
integer ATTACH_HAND = 6;                            // left hand = 5, right hand = 6
string SAFETY_OFF_MESSAGE = "The safety is now OFF."; //message when you touch it.
string SAFETY_ON_MESSAGE = "The safety is now ON.";        // a message when you... 
string NOT_OWNER = "You are not the owner of this weapon."; //message when non-owner touches it - assumes a sci-fi-like grip with owner ID
string ANIM_HOLD = "hold_R_handgun";                // animation to use when holding pistol
string ANIM_AIM  = "PistolRightSteady1";            // animation to use when aiming in mouselook
float  TIMER_RES = 0.5;                             // time in seconds to check for mouselook to start aim animation, like an AO
integer AVSTATE  = 0;                               // is av in mouselook?
vector OFFSET = <1.10, -0.25, 0.75>;                // rez offset for bullet
vector OFFSET_LEFT  = <1.10,  0.25, 0.75>;                // rez offset for bullet
vector OFFSET_RIGHT = <1.10, -0.25, 0.75>;                // rez offset for bullet
integer CHANMYRIAD = -999;                      // regionwide RPEVENT channel for Myriad
string DIV = "|";                               // field divider between parts of Myriad messages
// Myriad Ranged Weapons Damage Dice, PDF page 64, Myriad Special Edition page 98
// 1 die of damage = small stones thrown by hand (or in crude sling)
// 2 die of damage = throwing knife, derringer, hand catapult, thrown house brick
// 3 die of damage = throwing axe, pistol, hunting rifle, automatic pistol (full auto)
// 4 die of damage = very heavy pistol (Magnum), assault rifle (full auto), shotgun
// 5 die of damage = high velocity sniper rifle, heavy calibre machine gun (full auto)
integer DAMAGEDICE = 3;                             // many damage dice does this bullet do in Myriad? default to pistol here
integer MAXAMMO = 30; // 30 round clip

//===========================================================================
// GLOBAL RUNTIMES - runtime variables we change as we go
// Don't alter anything below if your not  familiar with it.
//===========================================================================
integer SAFETY_ON = TRUE;  // is this weapon safety on or off by default?
vector pos;         // holds our current position temporarily  
rotation rot;       // holds our current rotation temporarily
vector offset;      // holds an offset to where to rez the bullet based on position and rotation   
integer CHANATTACH = 0; // dynamic channel for attachment messages
integer HANDATTACH = 0; // chat channel handle for attachment dynamic channel
integer AMMOLEFT; // how much ammo is left?

//===========================================================================
// GLOBAL SETUP
//===========================================================================
SETUP() {
    CHANATTACH = (integer)("0x"+llGetSubString((string)llGetOwner(),1,7)); // calculate the dynamic attachment channel
    HANDATTACH = llListen(CHANATTACH,"",NULL_KEY,""); // start a listener on the attachment channel
    llWhisper(CHANATTACH,"ATTACHRANGED"+DIV+(string)DAMAGEDICE+DIV+(string)llGetAttached()+DIV+llGetObjectName()); // inform the player HUD that we're using a weapon to turn off fist fighter
    llOwnerSay("Attach me to your right hand, and enter mouselook to fire!");    
    // ask for permission to trigger animations, take controls, and attach to avatar
    llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS|PERMISSION_ATTACH);
    llPreloadSound(gunsound); // try pre-loading the gunsound for everyone around
    if ( SAFETY_ON == TRUE ) { // lets tell owner the state of the safety
        if ( llGetAttached() == 5 ) HOLSTER("left"); // safety on? start holstered
        if ( llGetAttached() == 6 ) HOLSTER("right"); // safety on? start holstered
    } else { // safety is not on
        if ( llGetAttached() == 5 ) DRAW("left"); // safety off! start drawn
        if ( llGetAttached() == 6 ) DRAW("right"); // safety off! start drawn
    }
    RELOAD(); // start the weapon with a full clip
    llSetTimerEvent(TIMER_RES); // start a timer running to check for mouselook and start aim animations or not
}

// SAFETY OFF - turn off safety and enable firing
SAFETYOFF() {
    SAFETY_ON = FALSE; // switch the safety off
    llOwnerSay(SAFETY_OFF_MESSAGE); // and tell them
}

// SAFETY ON - turn on safety
SAFETYON() {
    SAFETY_ON = TRUE; // switch the safety on
    llOwnerSay(SAFETY_ON_MESSAGE); // and tell them    
}

// DRAW THE WEAPON
DRAW(string hand) {
    // draw code goes here - turn copy in hand visible?
    if ( llGetAttached() == 5 && ( hand == "left" || hand == "both" ) ) {
        llRegionSay(CHANMYRIAD,llKey2Name(llGetOwner())+" draws a weapon with their left hand.");
        llOwnerSay("Left hand weapon drawn.");
        llSetLinkAlpha(LINK_SET,1.0,ALL_SIDES);        
    }
    if ( llGetAttached() == 6 && ( hand == "right" || hand == "both" ) ) {
        llRegionSay(CHANMYRIAD,llKey2Name(llGetOwner())+" draws a weapon with their right hand.");
        llOwnerSay("Right hand weapon drawn.");
        llSetLinkAlpha(LINK_SET,1.0,ALL_SIDES);        
    }
    SAFETYOFF(); // unholster and turn off safety
}

// HOLSTER THE WEAPON
HOLSTER(string hand) {
    // holster code goes here - turn copy in hand invisible?
    if ( llGetAttached() == 5 && ( hand == "left" || hand == "both" ) ) {
        llRegionSay(CHANMYRIAD,llKey2Name(llGetOwner())+" holsters their left-hand weapon.");
        llOwnerSay("Left hand weapon holstered.");
        llSetLinkAlpha(LINK_SET,0.0,ALL_SIDES);        
    }
    if ( llGetAttached() == 6 && ( hand == "right" || hand == "both" ) ) {
        llRegionSay(CHANMYRIAD,llKey2Name(llGetOwner())+" holsters their right-hand weapon.");
        llOwnerSay("Right hand weapon holstered.");
        llSetLinkAlpha(LINK_SET,0.0,ALL_SIDES);        
    }
    SAFETYON(); // turn on safety before holstering
}

// RELOAD THE WEAPON
RELOAD() {
    AMMOLEFT = MAXAMMO;
}

// CHECK AMMO LEFT
CHECKAMMO() {
    if ( AMMOLEFT > MAXAMMO ) { AMMOLEFT = MAXAMMO; } // don't overflow clip
    if ( AMMOLEFT < 0 ) { AMMOLEFT = 0; } // can't have negative bullets
    llOwnerSay("Ammo remaining: "+(string)AMMOLEFT);
}

//===========================================================================
// STATE DEFAULT - the main state is the default state.
// When a script is compiled, reset or loaded, this is the state it enters by default.
//===========================================================================
default {
    //-----------------------------------------------------------------------
    // STATE_ENTRY EVENT - Triggered on any state transition and start up
    //-----------------------------------------------------------------------
    state_entry() {
        SETUP(); // call global setup
    }

    //-----------------------------------------------------------------------
    // ON_REZ EVENT - Triggered when object attached or rezzed on the ground
    //-----------------------------------------------------------------------
    on_rez(integer rezparams) {
        SETUP();
    }

    //-----------------------------------------------------------------------
    // ATTACH EVENT - when the object is attached or detached
    //-----------------------------------------------------------------------
    attach(key id) {
        if ( id == NULL_KEY) { // if id is null key, this is a detach event
            llRegionSay(CHANMYRIAD,"RPEVENT"+DIV+llKey2Name(llGetOwner())+ " puts away their "+llGetObjectName());
            llWhisper(CHANATTACH,"DETACHRANGED"+DIV+(string)DAMAGEDICE+DIV+(string)llGetAttached()+DIV+llGetObjectName()); // inform the player HUD that we're using a weapon to turn on fist fighter
            integer perms = llGetPermissions(); // what permissions does object have
            if ( perms & PERMISSION_TRIGGER_ANIMATION ) { // if object has permission to change animations
                llStopAnimation(ANIM_HOLD); // stop the 'hold a weapon' animation since we're detaching
            }
            return;
        }
    }

    //-----------------------------------------------------------------------
    // RUN_TIME_PERMISSIONS EVENT - triggered when avatar grants permissions to object
    // some permissions automatically granted on attach or sit - but you should STILL check correctly for those
    //-----------------------------------------------------------------------
    run_time_permissions(integer perm) {

        if (perm & PERMISSION_ATTACH ) { // was object granted permission to attach?
            //llAttachToAvatar(ATTACH_HAND); // attach to the given attach point, 5 = left hand, 6 = right hand
        }
        if ( perm & PERMISSION_TAKE_CONTROLS ) { // was object granted permission to read control events?
            llTakeControls(CONTROL_ML_LBUTTON, TRUE, FALSE); // start reading the left mouse button in mouselook mode
        }
        if ( perm & PERMISSION_TRIGGER_ANIMATION ) { // was object granted permission to trigger animations?
            llStartAnimation(ANIM_HOLD); // start the 'hold a weapon' animation
        }
    }

    //-----------------------------------------------------------------------
    // TOUCH_START EVENT - trigger at the start of someone clicking on the object
    //-----------------------------------------------------------------------
    touch_start(integer touches) {  // Triggered by the start of agent clicking on object
        while (touches--) { // count down through each touch event
            key who = llDetectedKey(touches); // get the key of who triggered the touch
            if ( llGetOwner() != who ) { // if not the owner
                llInstantMessage(who,NOT_OWNER); // tell them
            } if ( SAFETY_ON == TRUE ) { // lets tell owner the state of the safety
                SAFETY_ON = FALSE;
                if ( llGetAttached() == 5 ) DRAW("left"); // safety off! start drawn
                if ( llGetAttached() == 6 ) DRAW("right"); // safety off! start drawn
            } else if (SAFETY_ON == FALSE ) { // safety is not on
                SAFETY_ON = TRUE;
                if ( llGetAttached() == 5 ) HOLSTER("left"); // safety on? start holstered
                if ( llGetAttached() == 6 ) HOLSTER("right"); // safety on? start holstered
            }
        } // end while
    }

    //-----------------------------------------------------------------------
    // CHANGED EVENT - various changes to object can trigger this event
    //-----------------------------------------------------------------------
    changed(integer change) {
        if(change & CHANGED_OWNER) { // did the object change owner through a give or drop/take?
            integer perms = llGetPermissions(); // what permissions does object have?
            if ( perms & PERMISSION_TRIGGER_ANIMATION ) { // if object has permission to change animations
                llStopAnimation(ANIM_HOLD); // stop the 'hold a weapon' animation'
            }
            llResetScript(); // and reset the entire script
        }
    }

    //-----------------------------------------------------------------------
    // EVENT CONTROL - called for user input we've requested with LLTAKECONTROLS
    //-----------------------------------------------------------------------
    control(key owner, integer level, integer edge) {
        if ( SAFETY_ON == TRUE ) { // check the safety before shooting
            llOwnerSay("The safety is ON."); // warn owner they are shooting with safety on
            return; // return now to prevent running the rest of the shooting code
        }
        integer pressed = level & edge; // level is when you click ,edge would be if you let up.
        if ( pressed & CONTROL_ML_LBUTTON ) { // if left mouse button was pressed in MouseLook mode
            if ( AMMOLEFT <= 0 ) { 
                llOwnerSay("Out of ammo! Reload!");
                return;
            }
            // Fire 1 bullet,,  the heart of the firearm script.
            pos = llGetPos(); // get our current position
            rot = llGetRot(); // get our current rotation
            offset = OFFSET; // start with the base offset for the gun held in the right hand
            if ( ATTACH_HAND == 5 ) { // is weapon in left hand?
                offset = OFFSET_LEFT; // use the left-hand offset
            }
            if ( ATTACH_HAND == 6 ) { // is weapon in right hand?
                offset = OFFSET_RIGHT; // use the right hand offset
            }
            offset *= rot; // now, rotate the offset to match the avatar rotation
            pos += offset; // now combine the rotated offset with avatar position 
            vector fwd = llRot2Fwd(rot); // calculate the direction that is "avatar's facing" 
            fwd *= BULLET_VELOCITY; // now multiply that by bullet speed to tell bullet to push in that direction, that fast
            rot *= llEuler2Rot(<0, PI_BY_TWO, 0>); // now, straighten rotation for object we're about to rez
            llPlaySound(gunsound,1.0); // here "gunsound"is a variable defined above.
            // DAMAGEDICE is passed to rez-param of bullet. Myriad Bullets read this as damage dice to do if they hit
            AMMOLEFT--;
            llRezObject(ammo, pos, fwd, rot, DAMAGEDICE); // does the actual work rezzes the ammo in the specified variables.
            llSleep(REPEAT_DELAY); // force a pause between shots
        }
    }

    //-----------------------------------------------------------------------
    // LISTEN EVENT - listen for whisper, say, shout, regionsay messages
    //-----------------------------------------------------------------------
    listen(integer channel, string name, key uuid, string message) {
        if ( channel == CHANATTACH ) { // did message come in on attachment channel?
            if ( message == "REGISTERATTACHMENTS" ) { // request from a HUD to register attachments already in use
                llWhisper(CHANATTACH,"ATTACHRANGED"+DIV+(string)DAMAGEDICE+DIV+(string)llGetAttached()+DIV+llGetObjectName()); // inform the player HUD that we're using a weapon to turn off fist fighter
                return;
            }
            if ( message == "DRAWLEFT" ) { DRAW("left"); return;} // draw pistol if in left hand
            if ( message == "DRAWRIGHT" ) { DRAW("right"); return;} // draw pistol in right hand
            if ( message == "DRAWBOTH" ) { DRAW("both"); return; } // draw pistols in both hands
            if ( message == "HOLSTERLEFT" ) { HOLSTER("left"); return;} // holster left-hand pistol
            if ( message == "HOLSTERRIGHT" ) { HOLSTER("right"); return;} // holster right-hand pistol
            if ( message == "HOLSTERBOTH" ) { HOLSTER("both"); return; } // holster both pistols
            if ( message == "SAFETYOFF" ) { SAFETYOFF(); return; } // unsafe the weapon
            if ( message == "SAFETYON" ) { SAFETYON(); return; } // safe the weapon
            if ( message == "CHECKAMMO" ) { CHECKAMMO(); return; } // check ammo left
            if ( message == "RELOAD" ) { RELOAD(); return; } // reload the clip            
        }
    }

    //-----------------------------------------------------------------------
    // TIMER EVENT - called regularly as an animation override for holding or aiming weapon
    //-----------------------------------------------------------------------
    timer() {
        if ( SAFETY_ON == TRUE ) return; // if safety is on, don't need to do weapon hold and aim animation
        AVSTATE = llGetAgentInfo(llGetOwner()); // get an integer that holds the current state of the avatar to see if they are in mouselook or not
        if ( AVSTATE & 0x0008 ) { // 0x0008 = AGENT_MOUSELOOK constant - this hack needed on OpenSim
            llStopAnimation(ANIM_HOLD); // stop the 'just hold a weapon' animation
            llSleep(0.05); // wait a little
            llStartAnimation(ANIM_AIM); // start the aiming weapon animation
        } else { // avatar is not in mouselook
            llStopAnimation(ANIM_AIM); // stop the aiming animation
            llSleep(0.05); // wait a little
            llStartAnimation(ANIM_HOLD); // now start the just holding a weapon animation
        }
        AVSTATE = 0; // clean up or reset the avatar state variable
    }
} // end of default
//============================================================================
// END
//============================================================================
Creative Commons License
This work by