HELP
Scripting Advanced Published: 2026-04-01  |  ← Back to School

Scripting Weapons: Projectiles and Damage

Scripting Weapons: Projectiles and Damage — Alife Virtual School

Scripting Weapons: Projectiles and Damage — Free class in Alife Virtual School

Welcome, creators, to Alife Virtual School! In the ever-expanding metaverse, the ability to create interactive and dynamic experiences is the ultimate currency. Few skills are more impactful than learning how to script weapons. This isn't just about combat; it's about game design, interactive storytelling, and pushing the boundaries of what's possible in a free 3D world. In this advanced tutorial, Scripting Weapons: Projectiles and Damage, we will embark on a deep dive into the LSL scripting functions that power the most engaging roleplay and combat systems across Alife Virtual. You will learn to harness the power of llRezObject for spawning projectiles, master vector calculations for realistic velocity, and implement collision detection to build damage-enabled systems that bring your virtual creations to life.

The Alife Advantage: Build Without Barriers

In many virtual worlds, the dream of building complex systems like a combat-ready weapon is often gated by significant real-world costs. Land fees, upload fees, and marketplace transaction fees create a financial barrier to entry. Alife Virtual was built on a different philosophy: pure, unhindered creation. Here, your only limit is your imagination. Let's compare the cost of developing and deploying the weapon we're about to build.

Feature / Action Alife Virtual Cost Competitors (e.g., Second Life)
Private Island for Development (65,536 sqm) $0 (Free, Forever) ~$300 USD / month
Uploading Weapon Mesh $0 (Free & Unlimited) L$10+ per upload (varies with complexity)
Uploading Projectile Texture $0 (Free & Unlimited) L$10 per upload
Uploading Firing Sound Effect $0 (Free & Unlimited) L$10 per upload
Total Cost to Build & Test $0 $300+ initial monthly cost, plus per-asset fees

This stark difference highlights the freedom you have in Alife Virtual. You can experiment, fail, and succeed without spending a dime. This makes our platform the ideal learning environment and a true haven for builders in the Open Simulator ecosystem, especially for those looking for a powerful Second Life alternative with a genuinely free virtual economy.

What You Will Learn

By the end of this master-level class, you will be able to:

Prerequisites

This is an Advanced scripting class. Before you begin, you should have a solid understanding of the following LSL concepts:

Part 1: Setting the Stage - The Weapon and the Projectile

Every weapon system needs two core components: the launcher (the gun, wand, etc.) and the projectile (the bullet, magic bolt, etc.).

  1. Create the Launcher: Rez a simple cube prim. This will be our "gun." Name it Weapon Launcher.
  2. Create the Projectile: Rez a small sphere prim. Make it visually distinct—perhaps bright red. Name this object Projectile.
  3. Prepare the Inventory:
    • In the Weapon Launcher's inventory, create a new script. Name it weapon_script.
    • Now, drag the Projectile object from the world into the Weapon Launcher's inventory. This places a copy of the projectile inside the launcher, ready to be rezzed. You can now delete the original Projectile from the world.

Your Weapon Launcher prim should now contain two items: the weapon_script and the Projectile object.

Part 2: The Launcher Script - Firing the Projectile

Open the weapon_script inside your Weapon Launcher. This script's job is to listen for a touch, calculate where the projectile should go, and then rez it with the correct velocity.

Erase the default content and insert the following code. We'll break it down line by line.

weapon_script.lsl


// The name of the projectile object in this prim's inventory.
string gProjectileName = "Projectile";

// How fast the projectile will travel (meters per second).
float gProjectileSpeed = 50.0;

// An offset to make the projectile appear in front of the avatar, not inside them.
vector gFireOffset = <1.0, 0.0, 0.0>; 

default
{
    state_entry()
    {
        // When the script starts, it asks for permission to take money.
        // This is a legacy requirement for rezzing objects from prim inventory.
        // In Alife Virtual's free economy, no money is ever taken.
        llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
    }

    run_time_permissions(integer perm)
    {
        // This event triggers after the owner responds to the permission dialog.
        if (perm & PERMISSION_DEBIT)
        {
            llOwnerSay("Weapon armed and ready. Click to fire.");
        }
    }

    touch_start(integer total_number)
    {
        // Get the avatar who touched the weapon.
        key toucher = llDetectedKey(0);

        // We only want the owner to be able to fire it.
        if (toucher == llGetOwner())
        {
            // --- Calculation Step ---
            // Get the avatar's current rotation.
            rotation userRot = llGetAgentInfo(toucher) & AGENT_ROTATION;
            
            // Calculate the forward direction vector based on the avatar's rotation.
            vector forwardVec = llRot2Fwd(userRot);
            
            // Calculate the projectile's velocity by multiplying direction by speed.
            vector projectileVel = forwardVec * gProjectileSpeed;
            
            // Calculate the starting position. Start at the avatar's position...
            vector startPos = llGetAgentInfo(toucher) & AGENT_POSITION;
            
            // ...then add an offset so it appears in front of them. The offset must also be rotated.
            startPos += gFireOffset * userRot;
            
            // --- Firing Step ---
            // Rez the projectile from inventory!
            llRezObject(gProjectileName, startPos, projectileVel, userRot, 0);
        }
    }
}

Launcher Script Breakdown:

Pro Tip: Object Permissions

Before proceeding, right-click your Weapon Launcher, go to Edit -> Content tab. Right-click the Projectile object in the inventory and select Properties. Ensure the "Next owner" permissions are set to Copy/Modify/Transfer. If the projectile is `no-copy`, llRezObject will fail unless you have PERMISSION_DEBIT and the object is also transferable.

Part 3: The Projectile Script - Collision and Cleanup

A projectile that flies forever is just space junk. A useful projectile needs to know when it hits something and then clean itself up. Let's create the script for our projectile object.

  1. Right-click the Weapon Launcher and go to Edit -> Content.
  2. Double-click the Projectile object in the inventory to open it.
  3. In the Projectile's build window, go to the Content tab and create a new script. Name it projectile_script.

Now, open projectile_script and insert the following code.

projectile_script.lsl


// A communication channel for damage messages.
integer gDamageChannel = -123456789;

// How many "points" of damage this projectile does.
float gDamageAmount = 10.0;

// How long the projectile lives before disappearing (in seconds).
float gLifeTime = 10.0;

// --- Private variables ---
key gOwnerKey;

// Function to handle the impact effects.
do_impact(key target)
{
    // Make a small explosion effect.
    llParticleSystem([
        PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_INTERP_SCALE_MASK,
        PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE,
        PSYS_PART_START_COLOR, <1.0, 0.5, 0.0>,
        PSYS_PART_END_COLOR, <0.5, 0.0, 0.0>,
        PSYS_PART_START_SCALE, <0.5, 0.5, 0.0>,
        PSYS_PART_END_SCALE, <2.0, 2.0, 0.0>,
        PSYS_PART_MAX_AGE, 0.5,
        PSYS_SRC_MAX_AGE, 0.2,
        PSYS_SRC_BURST_RATE, 0.1,
        PSYS_SRC_BURST_PART_COUNT, 10
    ]);
    
    // Check if we hit an avatar.
    if (llGetAgentSize(target) != ZERO_VECTOR)
    {
        // We hit an avatar! Send them a damage message.
        // The message format is: "damage_type,amount,attacker_key"
        string damageMessage = "kinetic," + (string)gDamageAmount + "," + (string)gOwnerKey;
        llRegionSayTo(target, gDamageChannel, damageMessage);
    }
    
    // The projectile has done its job, now it must be destroyed.
    llDie();
}


default
{
    on_rez(integer start_param)
    {
        // This event runs as soon as the object is rezzed.
        // First, get the key of the person who fired the weapon.
        gOwnerKey = llGetOwner();
        
        // Make the object physical so it can collide with things.
        llSetStatus(STATUS_PHYSICS, TRUE);
        
        // Set a timer to self-destruct if it doesn't hit anything.
        llSetTimerEvent(gLifeTime);
    }

    collision_start(integer num_detected)
    {
        // This event fires when the object collides with something.
        integer i = 0;
        for (i = 0; i < num_detected; i++)
        {
            // Get the UUID of the object we collided with.
            key detectedKey = llDetectedKey(i);
            
            // We don't want to hit the person who fired the weapon.
            if (detectedKey != gOwnerKey)
            {
                do_impact(detectedKey);
                // The llDie() in do_impact() will stop the script, so no need for a break.
            }
        }
    }

    timer()
    {
        // If the timer runs out, the projectile disappears.
        // This prevents lag from missed shots.
        llDie();
    }
}

Projectile Script Breakdown:

Once you've saved this script, close the projectile's edit window. Your weapon is now fully scripted! Wear the Weapon Launcher, wait for the permission request, grant it, and click to fire.

Common Mistakes to Avoid

  • Forgetting llDie(): The single biggest cause of sim lag in combat sims is orphaned physical projectiles. Always have a cleanup plan, both on collision and on a timer.
  • Incorrect Projectile Permissions: If your projectile is no-copy or no-transfer in its inventory properties, llRezObject will often fail silently. Always check the "next owner" permissions on the projectile inside the launcher.
  • Phantom Projectiles: If you forget llSetStatus(STATUS_PHYSICS, TRUE), your projectile will be "phantom" and pass through everything without triggering a collision.
  • Rezzing Inside Yourself: A zero or small offset vector will cause the projectile to spawn inside your avatar, triggering an immediate collision with yourself.

Advanced Applications and Further Learning

This basic system is a powerful foundation. Here are ways to expand upon it:

Practice Exercise

Challenge yourself to apply what you've learned. Modify the scripts to create a "Chain Lightning" bolt:

  1. When the projectile hits an avatar, it should still do damage as normal.
  2. After the initial impact, it should use llSensor to find one other avatar (not the owner and not the initial target) within 10 meters.
  3. If another avatar is found, use llRezObject again from the projectile itself to fire a *new* projectile at the second target.
  4. Hint: You will need to give the projectile a copy of itself in its own inventory to do this. You will also need to calculate a new velocity vector from the impact point to the new target.

Frequently Asked Questions (FAQ)

1. My weapon says "Armed and ready," but nothing happens when I click. Why?
This is almost always a problem with llRezObject failing silently. Check these three things: 1) Is the gProjectileName string in the script an exact match for the object name in inventory? 2) Are the "next owner" permissions for the projectile inside the launcher set to allow at least Copy and Modify? 3) Are you on land that disallows object creation ('no-build' land)?
2. How can I make the projectile fire from the tip of my gun model, not just in front of me?
This requires a more advanced offset calculation. A common method is to link a tiny, invisible prim to the tip of your gun barrel. In your script, you can get its position and rotation using llGetLinkPrimitiveParams and use that as the starting point for your startPos and userRot calculations, instead of using llGetAgentInfo.
3. How do I make my weapon fully automatic?
You can use the touch and touch_end events to control a timer. In touch_start, start a repeating timer with llSetTimerEvent(0.1) (for 10 shots per second). In the timer() event, place your firing code. In the touch_end event, turn the timer off with llSetTimerEvent(0.0).
4. How do I protect my final script so no one can steal my work?
When you are ready to distribute your weapon, you can change the script's permissions. In the script window, go to File -> Set Permissions. Uncheck "Modify" and "Transfer" for the next owner. This will make your script no-modify and no-transfer in the final product you sell or give away. Always keep a full-permission copy for yourself!

Your Creative Journey Starts Here. Build Without Limits.

You now possess the foundational knowledge to build complex, engaging, and exciting interactive items in the metaverse. The skills you've learned today are your passport to creating the next great combat game, roleplay adventure, or interactive art piece in Alife Virtual.

Why wait? The tools are free, the land is free, and a vibrant community of creators is waiting. Claim your FREE 65,536 sqm private island in Alife Virtual today! Experience the power of unlimited uploads, a professional mesh avatar from day one, and a 100% free economy. Stop paying to create and start creating. Welcome to the future of the free 3D world.


🎓 Ready to Practice In-World?

Get your FREE island and practice everything you just learned — no credit card, no monthly fees.

Claim Your Free Island Now →

No credit card required · Takes 2 minutes · Your island is FREE forever


Published: 2026-04-01 · Difficulty: Advanced · Category: Scripting  |  Questions? Contact us  |  ← Back to School