Premade Vehicle Scripts

From InWorldz Wiki
Jump to: navigation, search

The scripts presented here are all released under the Creative Commons Attributions license (CC-BY-SA 4.0). The details are described here: http://creativecommons.org/licenses/by-sa/4.0/

The purpose of these scripts is to get your vehicle project going quickly. That said there is one important part that has to be done right lest a lot of frustration will happen: you must align your vehicle properly. All this is explained in detail in the Beginners Vehicle Tutorial. The summary is to make a small cube, then link your vehicle to it so that the small cube is the root prim. Finally, drop in one of the vehicle scripts. If you do that, your vehicle should line up as expected. It should look like this:

aligning the root prim


The Standard Vehicle Types

When making LSL vehicles you have a choice of selecting various basic vehicle types. They are:

  1. VEHICLE_TYPE_SLED
  2. VEHICLE_TYPE_CAR
  3. VEHICLE_TYPE_MOTORCYCLE
  4. VEHICLE_TYPE_BOAT
  5. VEHICLE_TYPE_SAILBOAT
  6. VEHICLE_TYPE_AIRPLANE
  7. VEHICLE_TYPE_BALLOON

The motorcycle and sailboat vehicle types are InWorldz enhancements explained in the tutorials, but the primary difference is that the motorcycle works much better at staying upright than a car and has better leaning characteristics. The sailboat is tuned to react better than a boat to banking and it also reacts to the region winds without explicit scripting.

Universal Vehicle Script

Though it is often better to make vehicle scripts tuned for one vehicle type, there is still great utility in having a universal vehicle script. For one, it is a great teaching tool in how the different vehicle types interact. The following code is that universal vehicle script. It is deeply commented to explain how the different parts work. As written, you need only decide whether the vehicle should be operable by only the owner or everyone. Then choose the vehicle type, and finally the sit target. The rest is automatically determined. The script handles several input control methods depending on the vehicle type.

//  Generic physical vehicle
//  2014, Balpien Hammerer
//  Licensed under Creative Commons CC-BY 4.0


// NOTE: Read through and adjust the customization choices.
// ======================================================================================
//

//  Turning on debugging will cause various messages to be sent to you (the owner) to help
//  you with your development. LeaveDebug FALSE in your production vehickes.
integer Debug       = FALSE;


//  When OwnerOnly is set to TRUE, only the owner of the vehicle can ride it.
//  When OwnerOnly is set to FALSE, then anyone can ride the vehicle.
//
//  WARNING: if you decide to sell a vehicle as COPY but you let anyone ride the
//  vehicle, you will have just given away your vehicle forever because one person can then
//  rez a fleet of the one vehicle and create a very nice rental business for themselves.
//
integer OwnerOnly   = TRUE;

//  Choose one of the vehicle types by uncommenting one of the following types. When a
//  vehicle type is set, the system also sets the default parameters for that type.
//  (remove one of the '//'):
//
integer VehicleType =
    //VEHICLE_TYPE_SLED;
    //VEHICLE_TYPE_CAR; 
    //VEHICLE_TYPE_MOTORCYCLE; 
    //VEHICLE_TYPE_BOAT; 
    //VEHICLE_TYPE_SAILBOAT; 
    VEHICLE_TYPE_AIRPLANE; 
    //VEHICLE_TYPE_BALLOON; 


//  There are several common input controls settings for vehicles and the method chosen depends
//  on what you wish to achieve. 
//      Method 0 (no controls) is for free moving vehicles, often used in sleds, 
//      free flying balloons or self-guided vehicles. The avatar cannot steer the vehicle.
// 
//      Method 1 is often used in land and powered seagoing vehicles. It has basic forward/back,
//      turn left/right and speed based on gears (speed ranges).
//
//      Method 2 is used in hybrid land, sea, air vehicles. It has the basic forward/back,
//      turn left/right and hover up/down controls. Also has strafing (side-to-side movement).
//
//      Method 3 is often used in aircraft. It has pitch up/down, bank left/right, and
//      throttle up/down controls.

// Method 0:        No input controls.

// Method 1:        A or left-arrow to turn left
//                  D or right-arrow to turn right
//                  W or up-arrow to move forward faster
//                  S or down-arrow to move forwrd slower or backward
//                  E or PgUp to gear up (higher top speed)
//                  C or PgDn to gear down (lower top speed)

// Method 2:        A or left-arrow to turn left
//                  D or right-arrow to turn right
//                  shift-A or shift-left-arrow to strafe left
//                  shift-D or shift-right-arrow to strafe right
//                  W or up-arrow to move forward faster
//                  S or down-arrow to move forward slower or backward
//                  E or PgUp to gain altitude
//                  C or PgDn to lose altitude

// Method 3:        A or left-arrow to bank left
//                  D or right-arrow to bank right
//                  W or up-arrow to pitch downward
//                  S or down-arrow to pitch upward
//                  E or PgUp to increase speed
//                  C or PgDn to decrease speed
integer InputMethod = 1;

//  The all-stop feature is very useful to people who wish to stop a vehicle immediately.
//  It also is good for newcomers to virtual vehicles, reduces frustration. When enabled,
//  pressing either W,S together or up/dn arrow keys together will stop the motors.
integer AllStop         = TRUE;

//  The follow parameters determine how quickly your vhicle moves in its various linear axes.
//  These speeds are in meters per second.
//  Adjust to your needs:

//  MaxFwdSpeed is the fastest speed the forward motor will go.
float   MaxFwdSpeed     = 30.0;

//  The increment is the amount to add every control event when speeding up.
//  The dreent is the amount of speed to remove every control event when slowing down.
float   IncrementFwd    = 0.2;
float   DecrementFwd    = 0.3;

//  If Method 1 was chosen, these are the speeds for each gear. You can add more speeds or fewer.
//  Up to you :)
list    Gears       = [MaxFwdSpeed*0.2, MaxFwdSpeed*0.4, MaxFwdSpeed*0.6, MaxFwdSpeed*0.7, MaxFwdSpeed];

//  MaxRevSpeed is the fastest spped the reverse motor will go (often much lower than the forward speed).
float   MaxRevSpeed     = -5.0;

//  MaxSideSpeed is the fastest that the strafing (side-to-side) otors will go.
float   MaxSideSpeed    = 3.0;

//  MaxVertSpeed is the fastest speed for up/down motors.
float   MaxVertSpeed    = 6.0;

//  The following parameters determine how quickly your vehicle rotates along the angular axes.
//  These speeds are best expressed in radians per second where, for example, 
//  PI is one complete turn in two seconds.
//  Adjust to your needs:

// Minimum turing speed ratio (0.0 to 1.0)
float   MinTurnRatio    = 0.4;

// Maximum left/right turning speed.
float   MaxYawSpeed     = PI/2;

// Maximum pitch (nose up/down) turning speed.
float   MaxPitchSpeed   = PI/4;

// Maximum banking speed.
float   MaxBankSpeed    = PI/2;

// Enable auto buoyancy for airplanes by setting it to nonzero. it simulates wing lift.
float   AutoBuoyancy    = 0;


// Sit target. You can set the sit target here if the avatar is to sit on the root prim
// of the vehicle. If the vehicle is large, you often want to sit the driver on a child prim
// and provide a sit script to do that. In that situation, set the SitPosition and vector
// to ZERO_VECTOR.
//vector  SitPosition     = ZERO_VECTOR;
//vector  SitRotation     = ZERO_VECTOR;
vector  SitPosition     = <0.0, 0.0, 0.5>;
vector  SitRotation     = ZERO_VECTOR;

// These settings are for the basic dynamic camera.
// Set them all to 0.0 if you do not want a dynamic camera.
float   CameraPitch     = 24.0;
float   CameraDistance  = 2.7;
float   CameraLag       = 0.2;


// Advanced special sauce. This is where you customize the behavior of the vehicle.
// The default settings work for most cases, but if you need tighter friction, zippier
// banking, etc., this is where you add the vehicle parameters.
// The vehicle type is passed to this function in case you have a hybrid vehicle that
// changes its type on the fly. You can then set the parameters based on the type.
//
SetAdvancedVehicleParameters(integer vtype)
{
    // The code here is an example for a multi-use vehicle system. Parameters have been chosen
    // to work best for each vehicle type.
    //
    /**/ if (vtype == VEHICLE_TYPE_CAR)
    {
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_TIMESCALE,<0.1, 0.3, 1000.0>);
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE,<20.0, 0.3, 0.3>);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE,<0.3, 0., 0.1>);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE,<0.1, 0.1, 0.1>);
        MaxFwdSpeed = 30.0;
        MaxYawSpeed = PI/2;
        MaxBankSpeed = PI/2;
        InputMethod = 1;
        AutoBuoyancy = 0;
    }
    
    else if (vtype == VEHICLE_TYPE_BOAT)
    {
        // This is a power boat with active z-turning and some banking.
        MaxFwdSpeed = 8.0;
        MaxYawSpeed = PI/4;
        MaxBankSpeed = PI;
        InputMethod = 2;
        AutoBuoyancy = 0;
    }
    
    else if (vtype == VEHICLE_TYPE_AIRPLANE)
    {
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_TIMESCALE,<0.1, 0.3, 0.3>);
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE,<20.0, 0.3, 0.3>);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE,<0.1, 0.1, 0.1>);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE,<0.01, 0.01, 1.0>);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE,<0.1, 0.1, 0.3>);
        llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 0.5);
        llSetVehicleFloatParam(VEHICLE_BANKING_TIMESCALE, 0.2);
        llSetVehicleFloatParam(VEHICLE_INVERTED_BANKING_MODIFIER, -0.75);
        MaxFwdSpeed = 40.0;
        MaxYawSpeed = 0;
        MaxBankSpeed = PI/2;
        MaxPitchSpeed = PI/2;
        InputMethod = 3;
        AutoBuoyancy = MaxFwdSpeed * 0.5;
        llSetTimerEvent(1.0);
    }
    
    else if (vtype == VEHICLE_TYPE_BALLOON)
    {
        // Balloons and airships are slow. For controllability, the z-motor decay is low.
        llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 1000);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE,<1.0, 1.0, 0.1>);
        MaxFwdSpeed = 5.0;
        MaxYawSpeed = PI/16;
        MaxBankSpeed = PI/16;
        InputMethod = 2;
        AutoBuoyancy = 0;
        
        // Note also balloons are pushed by the winds. Tis can be turned off by uncommenting this:
        // llSetVehicleVectorParam(VEHICLE_LINEAR_WIND_EFFICIENCY, <0,0,0>);
    }
}

//
// ======================================================================================



SetCameraParams()
{
    if (llGetPermissions() & PERMISSION_CONTROL_CAMERA)
    {
        if (CameraPitch != 0 || CameraDistance != 0 || CameraLag != 0)
        {
            // These parameters keeps the camera close in on slow movement but backs off
            // when traqvelling quickly. What this does is make steering vastly easier during
            // high speed runs.
            llSetCameraParams( [CAMERA_ACTIVE, TRUE, 
                                CAMERA_FOCUS_LOCKED, FALSE,
                                CAMERA_FOCUS_THRESHOLD, 0.3,
                                CAMERA_FOCUS_OFFSET, <0.0, 0.0, 0.5>,
                                CAMERA_PITCH, CameraPitch,
                                CAMERA_DISTANCE, CameraDistance,
                                CAMERA_BEHINDNESS_ANGLE, 0.6,
                                CAMERA_POSITION_THRESHOLD, 0.2,
                                CAMERA_POSITION_LAG, CameraLag]);
        }
        
        else
        {
            llClearCameraParams();
        }
    }
}

Engage(key id)
{
    // Someone got on
    if (id != NULL_KEY)
    {
        // If the driver is already sitting, ignore any other
        // sits
        if (SittingAgent != NULL_KEY) return;
         
        // OwnerOnly determines if anyone can ride the vehicle or solely the owner.
        if (!OwnerOnly || id == llGetOwner())
        {
            // Request desired permissions. Controls are requested only when
            // a movement method is chosen.
            integer desiredperms = PERMISSION_CONTROL_CAMERA;
            if (InputMethod != 0) desiredperms += PERMISSION_TAKE_CONTROLS;
            llRequestPermissions(id, desiredperms);
            
            // Notify FX scripts the vehicle has been engaged (started).
            llMessageLinked(LINK_SET, TRUE, "engage", id);
        
            SittingAgent = id;
        }
        else
        {
            // Unseat the person, notify them they are not the vehicle's owner.
            llMessageLinked(LINK_SET, FALSE, "engaged", id);
            llInstantMessage(id, "You are not the owner.");
            llUnSit(id);
        }
    }
    
    // Got off
    else
    {
        // If the vehicle has multiple riders, only respond to the
        // driver getting off.
        if (SittingAgent == NULL_KEY) return;
        SittingAgent = NULL_KEY;
        
        llSetStatus(STATUS_PHYSICS, FALSE);
        llReleaseControls();
        llClearCameraParams();
        
        // Work around an IW bug where permissions are not revoked when
        // an avatar stands;
        llRequestPermissions(llGetOwner(), 0);
        
        //Notify FX scripts the vehicle has been disengaged (stopped).
        llMessageLinked(LINK_SET, FALSE, "engage", id);
    }
}


// Handle a request to speed up
vector  MotorFwdFaster(vector localvel)
{
    // Stop jack rabbit motors by testing the local velocity against the
    // desired speed. If there is a large discrepency throttle back.
    if (llFabs(localvel.x) < 0.01) CurrentSpeed = 0;
    
    // For low friction vehicles, monitor the speed difference closely.
    if (VehicleType == VEHICLE_TYPE_AIRPLANE || VehicleType == VEHICLE_TYPE_BALLOON ||
        VehicleType == VEHICLE_TYPE_BOAT || VehicleType == VEHICLE_TYPE_SAILBOAT)
    {
        if (localvel.x < CurrentSpeed * 0.8) CurrentSpeed = localvel.x;
    }
    
    if (CurrentSpeed < 0) CurrentSpeed = 0;
    
    float   TopSpeed = MaxFwdSpeed;
    
    // Use gears if method 1
    if (InputMethod == 1)
    {
        TopSpeed = llList2Float(Gears, CurrentGear);
    }
    
    CurrentSpeed += IncrementFwd;
    if (CurrentSpeed > TopSpeed) CurrentSpeed = TopSpeed;
    
    if (Debug) llOwnerSay("lvel=" + (string)localvel + " cspd=" + (string)CurrentSpeed);
    
    return <CurrentSpeed, 0, 0>;
}

// Handle a request to slow down
vector  MotorFwdSlower(vector localvel)
{
    // In going slower it is possible to go into reverse.
    // If you do not want reverse to happen, set the reverse speed to zero.
    if (llFabs(localvel.x) < 0.01) CurrentSpeed = 0;
    
    CurrentSpeed -= DecrementFwd;
    if (CurrentSpeed < MaxRevSpeed) CurrentSpeed = MaxRevSpeed;
    
    if (Debug) llOwnerSay("lvel=" + (string)localvel + " cspd=" + (string)CurrentSpeed);
    
    return <CurrentSpeed, 0, 0>;
}


//  Internal variables - no touch.
integer CurrentGear     = 0;
float   CurrentSpeed    = 0;
float   Debounce;
key     SittingAgent    = NULL_KEY;

default
{
    state_entry()
    {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetVehicleType(VEHICLE_TYPE_NONE);
        
        // Set a sit target only if specified
        if (SitPosition != ZERO_VECTOR)
        {
            llSitTarget(SitPosition, llEuler2Rot(SitRotation));
        }
        else
        {
            // llSitTarget(ZERO_VECTOR, ZERO_ROTATION);
        }
        
        // Notify FX scripts the vehicle is stopped.
        llMessageLinked(LINK_SET, FALSE, "engage", NULL_KEY);
        
        // Unsit the avatar (this also works around an IW controls bug).
        llUnSit(llAvatarOnSitTarget());
    }
    
    on_rez(integer start)
    {
        // Work around some IW properties bugs.
        llStopMoveToTarget();
        llStopLookAt();
        llStopHover();
        llSetVehicleType(VEHICLE_TYPE_NONE);
        llRequestPermissions(llGetOwner(), 0);
        
        // Make your vehicle temp then save to inventory to permit rezzing it in a scenic.
        // Upon rezzing this attenpts to make it non-temp (works in full regions), 
        // but it remains remains temp in scenics.
        llSetPrimitiveParams([PRIM_TEMP_ON_REZ, FALSE]);
    }
    
    changed(integer chg)
    {
        if (chg & CHANGED_LINK)
        {
            // If a sit target is specified process it. Otherwise,
            // some other script is desingated to be the 'sitter', so do
            // not interfere with it.
            if (SitPosition != ZERO_VECTOR)
            {
                key     id  = llAvatarOnSitTarget();
                Engage(id);
            }
        }
    }
    
    run_time_permissions(integer perms)
    {
        // Test each permission individually because permissions are not always bundled.
        //
        if (perms & PERMISSION_TAKE_CONTROLS)
        {
            // Take over all the movement related input controls.
            llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | 
                           CONTROL_LEFT | CONTROL_RIGHT | CONTROL_UP | CONTROL_DOWN, TRUE, FALSE);

            // Make the vehicle physical and set the vehicle to the desired type.
            llSetStatus(STATUS_PHYSICS, TRUE);
            llSetVehicleType(VehicleType);
            
            // Advanced special sauce
            SetAdvancedVehicleParameters(VehicleType);
        }
        
        if (perms & PERMISSION_CONTROL_CAMERA)
        {
            // Take over the avatar's camera.
            SetCameraParams();
        }
    }

    control(key id, integer level, integer edge)
    {
        float   nowtime   = llGetTime();
        integer mouselook = (llGetAgentInfo(id) & AGENT_MOUSELOOK) != 0;
        vector  vel       = llGetVel();
        vector  localvel  = vel / llGetRot();
        vector  angular;
        vector  linear;
        
        if (Debug) llOwnerSay("control method=" + (string)InputMethod + " lvl=" + (string)level + " edge=" + (string)edge);

        // Nothing to do if the input method is no controls.
        if (InputMethod == 0) return;
        
        // If all-stop keys are being debounced, ignore the controls for a while.
        if (Debounce > nowtime) return;
        
        // If the all-stop feature was chosen and the all-stop control keys are pressed, stop the
        // vehicle motors immedately. This is not realistic but it helps reduce frustration
        // in new-to-virtual-vehicles users.
        if (AllStop && (level & (CONTROL_FWD | CONTROL_BACK)) == (CONTROL_FWD | CONTROL_BACK))
        {
            Debounce = nowtime + 0.5;
            level = 0;
            CurrentSpeed = 0;
            llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <0,0,0>);
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, <0,0,0>);
        }
        
        // Determine initial key presses and final key releases.
        integer pressed  = level & edge;
        integer released = ~level & edge;
        
        //Send control pressed/released messages to FX scripts (useful for fancy FX behaviors)
        if (pressed)    llMessageLinked(LINK_SET, pressed, "controls-pressed", id);
        if (released)   llMessageLinked(LINK_SET, released, "controls-released", id);
        
        // Some of the controls are remapped depending on the input method, so this control
        // key testing logic is a bitmore complex because of the differences.
        //
        if (level & CONTROL_FWD)
        {
            /**/ if (InputMethod == 1 || InputMethod == 2)
            {
                linear += MotorFwdFaster(localvel);
            }
            
            else if (InputMethod == 3)
            {
                angular += <0,MaxPitchSpeed,0>;
            }
        }
        
        if (level & CONTROL_BACK)
        {
            /**/ if (InputMethod == 1 || InputMethod == 2)
            {   
                linear += MotorFwdSlower(localvel);
            }
            
            else if (InputMethod == 3)
            {
                angular += <0,-MaxPitchSpeed,0>;
            }
        }
        
        // Side to side movement only happens out of mouselook view.
        if (!mouselook)
        {
            if (level & CONTROL_LEFT)
            {
                linear += <0,MaxSideSpeed,0>;
            }
            
            if (level & CONTROL_RIGHT)
            {
                linear += <0,-MaxSideSpeed,0>;
            }
        }
        
        if (level & CONTROL_UP)
        {
            /**/ if (InputMethod == 1)
            {
                integer maxgears = llGetListLength(Gears);
                ++CurrentGear;
                if (CurrentGear >= maxgears) --CurrentGear;
                
                // Notify FX scripts of the gear change
                llMessageLinked(LINK_SET, CurrentGear, "gear-up", id);
            }
            
            else if (InputMethod == 2)
            {
                linear += <0,0, MaxVertSpeed>;
            }
            
            else if (InputMethod == 3)
            {
                linear += MotorFwdFaster(localvel);
            }
        }
        
        if (level & CONTROL_DOWN)
        {
            /**/ if (InputMethod == 1)
            {
                integer maxgears = llGetListLength(Gears);
                --CurrentGear;
                if (CurrentGear < 0) CurrentGear = 0;
                
                // Notify FX scripts of the gear change.
                llMessageLinked(LINK_SET, CurrentGear, "gear-down", id);
            }
            
            else if (InputMethod == 2)
            {
                linear += <0,0,-MaxVertSpeed>;
            }
            
            else if (InputMethod == 3)
            {
                linear += MotorFwdSlower(localvel);
            }
        }
        
        // Compute the turning adjustment, based on the forward speed of the vehicle.
        // This makes them much more controllable.
        float   turnadj = MinTurnRatio + llFabs(localvel.x) / MaxFwdSpeed;
        if (turnadj > 1.0) turnadj = 1.0;
        
        // Adust using the turning controls depending on mouselook view. What this does is
        // make going into and out of mouselook view seamless in terms of the controls.
        //
        if (level & CONTROL_ROT_LEFT || level & (mouselook * CONTROL_LEFT))
        {
            /**/ if (InputMethod == 1)
            {
                angular += <0,0, MaxYawSpeed * turnadj>;
            }
            
            else if (InputMethod == 2)
            {
                angular += <-MaxBankSpeed * turnadj, 0, MaxYawSpeed * turnadj>;
            }
            
            else if (InputMethod == 3)
            {
                angular += <-MaxBankSpeed * turnadj, 0, 0>;
            }
        }
        
        if (level & CONTROL_ROT_RIGHT || level & (mouselook * CONTROL_RIGHT))
        {
            /**/ if (InputMethod == 1)
            {
                angular += <0,0, -MaxYawSpeed * turnadj>;
            }
            
            else if (InputMethod == 2)
            {
                angular += <MaxBankSpeed * turnadj, 0, -MaxYawSpeed * turnadj>;
            }
            
            else if (InputMethod == 3)
            {
                angular += <MaxBankSpeed * turnadj, 0, 0>;
            }
        }
        
        
        // Apply motors
        if (linear != ZERO_VECTOR)
        {
            llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, linear);
        }
        
        if (angular != ZERO_VECTOR)
        {
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, angular);
        }
    }
    
    timer()
    {
        if (AutoBuoyancy == 0)
        {
            llSetTimerEvent(0);
            return;
        }
        
        // Get local speed.
        vector  vel       = llGetVel();
        vector  localvel  = vel / llGetRot();
        
        // Set the buoyancy based on the ratio of the local forward speed
        // and the lift speed.
        float   buoy = llFabs(localvel.x) / AutoBuoyancy;
        if (buoy > 1.0) buoy = 1.0;
        
        llSetVehicleFloatParam(VEHICLE_BUOYANCY, buoy);
    }
    
    link_message(integer sender, integer num, string msg, key id)
    {
        if (msg == "sit-target")
        {
            Engage(id);
        }
    }
}


The Sled

The Car

The Motorcycle

The motorcycle vehicle type is basically a car with default parameters tuned to make it stay upright but lean and turn quickly when x-axis motors are engaged. the motorcycle type (an InWorldz enhancement) also has some internal differences expressing behaviors much more like a real motorcycle. The details are described in the Advanced Vehicle Tutorial. Watch this video to get a sense of how the motorcycle script works:

An InWorldz motorcycle script.

The Power Boat

The Sail Boat

The Airplane

The Balloon or Airship

The balloon is usually a motor-less free floating vehicle with some positive buoyancy so that it lifts up on its own. The simplest script to achieve this behavior follows. Place this script in a sphere and watch it rise then drift away pushed by the region winds:


// The simplest balloon script.
//  2014, Balpien Hammerer
//  This code is release under Creative Commons CC0 Universal
//  http://creativecommons.org/publicdomain/zero/1.0/
default
{
    state_entry()
    {
        llSetStatus(STATUS_PHYSICS, TRUE);
        llSetVehicleType(VEHICLE_TYPE_BALLOON);
        
        
        // Set the hover height 5m above whereever it is presently located.
        vector  pos = llGetPos();
        llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, pos.z + 5.0);
        llSetVehicleFlags(VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);
    }
}


The balloon can also be a powered vehicle with motors. A classic airship is a good example of the later. Watch this video to get a sense of how the following full-featured script performs:

An airship driven by the balloon script.


//  Sit & recline, Copyright 2013, Balpien Hammerer
//  This script is licensed under Creative Commons, CC4 BY-SA
//  http://creativecommons.org/licenses/by-sa/4.0/

// 2013/10/16 - fix altitude lock
// 2014/09/20 - Add OwnerOnly switch
// 2014/09/23 - Fix motion control
//  

//
// The balloon script is set to not engage when sat upon. Because balloons are
// often large strutures, it is better to set up a child prim that is the sit spot.
// Use the on/off script to engage this vehicle script.
//
// Balloons usually slip and slide through the air, but that can make the task of level
// flight difficult. Set the altitude lock to TRUE to have the script lock the altitude once
// set.
//
// Controls are:    left-arrow (or A) to turn left
//                  right-arrow (or D) to turn right
//                  up-arrow (or W) to move forward
//                  down-arrow (or S) to move backward
//                  PgUp (or E) to gain altitude
//                  PgDn (or C) to lose altitude


// This are the settings for the dynamic camera. If you do not
// want any scripted camera action, or if you have another script
// doing that, then set all of these values to 0.0
float   CameraPitch     =30.0; // 18.0;
float   CameraDistance  = 5.0; // 2.7;
float   CameraLag       = 0.1; // 0.2;

// When set to TRUE, the balloon will lock itself to a fixed altitude.
// The lock goes off when going up or down, and the time to relock, after
// the up or down key is pressed, is set in the LockTime variable (in seconds).
integer AltitudeLock    = TRUE;
float   AltitudeLockTime= 3.0;

// Set this to TRUE if you want only you, the owner, to use the balloon.
integer OwnerOnly       = TRUE;

// This is the sit target, the offset and rotation from the center of the root
// prim. If you have another script doing that, then set these values to ZERO_VECTOR.
vector  SitPosition     = ZERO_VECTOR;
vector  SitRotation     = ZERO_VECTOR;

// These value set the maximum forward, reverse and sideways speeds (in meters/second).
float   MaxFwdSpeed     =  7.0;
float   MaxRevSpeed     = -4.0;
float   MaxSideSpeed    =  2.0;
float   MaxVertSpeed    = 15.0;


// ------------------------------------------------------------------------------

SetCameraParams()
{
    if (llGetPermissions() & PERMISSION_CONTROL_CAMERA)
    {
        if (CameraDistance == 0 && CameraPitch == 0)
        {
            llSetCameraParams( [CAMERA_ACTIVE, FALSE]);
            return;
        }
    
        // These parameters keeps the camera close in on slow movement but backs off
        // when traqvelling quickly. What this does is make steering vastly easier during
        // high speed runs.
        llSetCameraParams( [CAMERA_ACTIVE, TRUE, 
                            CAMERA_FOCUS_LOCKED, FALSE,
                            CAMERA_FOCUS_THRESHOLD, 0.3,
                            CAMERA_FOCUS_OFFSET, <0.0, 0.0, 1.4>,
                            CAMERA_PITCH, CameraPitch,
                            CAMERA_DISTANCE, CameraDistance,
                            CAMERA_BEHINDNESS_ANGLE, 0.6,
                            CAMERA_POSITION_THRESHOLD, 0.2,
                            CAMERA_POSITION_LAG, CameraLag]);
    }
}

Engage(key id)
{
    // Got on
    if (id != NULL_KEY)
    {
        // If owneronly check sitting agent is owner.
        if (!OwnerOnly || id == llGetOwner())
        {
            llRequestPermissions(id, PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);
            llMessageLinked(LINK_SET, TRUE, "engage", id);
            llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, 0);
        }
        
        else
        {
            llUnSit(id);
        }
    }
    
    // Got off
    else
    {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llReleaseControls();
        llClearCameraParams();
        llMessageLinked(LINK_SET, FALSE, "engage", id);
        llSetTimerEvent(0);
        llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, 0);
    }
}

HandleControls(key id, integer level, integer edge)
{
    vector  lvel = llGetVel() / llGetRot();   // Current local velocity
    vector  angular;
    vector  linear;
    
    if (debounce && (level & (CONTROL_FWD | CONTROL_BACK))) return;
    else debounce = FALSE;
    
    if ((level & (CONTROL_FWD | CONTROL_BACK)) == (CONTROL_FWD | CONTROL_BACK))
    {
        debounce = TRUE;
        level = 0;
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <0,0,0>);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, <0,0,0>);
    }
    
    integer pressed  = level & edge;
    integer released = ~level & edge;
    
    //llOwnerSay("controls " + (string)level + " " + (string)edge);
    //Send control message to other scripts (useful for FX behaviors)
    if (pressed)    llMessageLinked(LINK_SET, pressed, "controls-pressed", id);
    if (released)   llMessageLinked(LINK_SET, released, "controls-released", id);
    
    if (level & CONTROL_FWD)
    {
        linear += <MaxFwdSpeed,0,0>;
    }
    
    if (level & CONTROL_BACK)
    {
        linear += <MaxRevSpeed,0,0>;
    }
    
    if (level & CONTROL_LEFT)
    {
        linear += <0,MaxSideSpeed,0>;
    }
    
    if (level & CONTROL_RIGHT)
    {
        linear += <0,-MaxSideSpeed,0>;
    }
    
    if (level & CONTROL_UP)
    {
        linear += <0,0,MaxVertSpeed>; 
        if (AltitudeLock)
        {
            llSetTimerEvent(AltitudeLockTime);
            llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 1000.0);
        }
    }
    
    if (level & CONTROL_DOWN)
    {
        linear += <0,0,-MaxVertSpeed>;
        if (AltitudeLock)
        {
            llSetTimerEvent(AltitudeLockTime);
            llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 1000.0);
        }            
    }
    
    // Linear speed adjustments
    // Assume that for any motor not set, that the vehicle shold continue
    // to go at the current in the respective axis.
    if (linear.x == 0) linear.x = lvel.x;
    if (linear.y == 0) linear.y = lvel.y;
    if (linear.z == 0) linear.z = lvel.z;
    
    if (level & CONTROL_ROT_LEFT)
    {
        angular += <-PI/64, 0, PI/24>;
    }
    
    if (level & CONTROL_ROT_RIGHT)
    {
        angular += <PI/64, 0, -PI/24>;
    }
    
    // Apply motors
    if (linear != ZERO_VECTOR)
    {
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, linear);
    }
    
    if (angular != ZERO_VECTOR)
    {
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, angular);
    }
}

integer debounce;

default
{
    state_entry()
    {
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetVehicleType(VEHICLE_TYPE_NONE);
        llSitTarget(ZERO_VECTOR, ZERO_ROTATION);
        
        // Set a sit target only if specified
        if (SitPosition != ZERO_VECTOR)
        {
            llSitTarget(SitPosition, llEuler2Rot(SitRotation));
        }
        
        llMessageLinked(LINK_SET, FALSE, "engage", NULL_KEY);
        llUnSit(llAvatarOnSitTarget());
    }
    
    changed(integer chg)
    {
        if (chg & CHANGED_LINK)
        {
            // If a sit target is specified process it.
            if (SitPosition != ZERO_VECTOR)
            {
                key     id  = llAvatarOnSitTarget();
                Engage(id);
            }          
        }
    }
    
    run_time_permissions(integer perms)
    {
        if (perms & PERMISSION_TAKE_CONTROLS)
        {
            llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | 
                           CONTROL_LEFT | CONTROL_RIGHT | CONTROL_UP | CONTROL_DOWN, TRUE, FALSE);

            llSetStatus(STATUS_PHYSICS, TRUE);
            llSetVehicleType(VEHICLE_TYPE_BALLOON);
            
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE,<2.0, 2.0, 0.3>);
            llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE,<0.3, 0.3, 1.0>);
            
            llSetVehicleVectorParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE, <2.0, 0.5, 1.0>);
            
            llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 1000.0);
            
            llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 4.0);
            llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.5);
            
            llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY, 0.05);
            llSetVehicleFloatParam(VEHICLE_BANKING_MIX, 0.0);
            
            llSetVehicleFloatParam(VEHICLE_BUOYANCY, 1.0);
            llSetVehicleFlags(VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT);
            vector  pos = llGetPos();
            llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, pos.z);
            llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 100.0);
            llSetTimerEvent(0.1);
        }
        
        if (perms & PERMISSION_CONTROL_CAMERA)
        {
            SetCameraParams();
        }
    }
    
    // This timer is called when altitude lock is enabled. Usually this is off for 
    // hot air balloons but enabled for airships like blimps or zepellins.
    timer()
    {
        vector  pos = llGetPos();
        llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, pos.z);
        llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 100.0);
        llSetTimerEvent(0);
    }

    // Control keys from the pilot
    control(key id, integer level, integer edge)
    {
        HandleControls(id, level, edge);
    }
    
    link_message(integer sender, integer num, string msg, key id)
    {
        if (msg == "sit-target")
        {
            Engage(id);
        }
        
        if (msg == "controls")
        {
            HandleControls(id, num, 0);
        }
    }
}

In the case where the balloon script is set up to be remotely engaged, the prim where the avatar sits needs to have in it the following sit script. This script sends a message to the vehicle script when the avatar sits on the child prim. It also starts the 'stand' animation:

//  Sit & Engage, Copyright 2014, Balpien Hammerer
//  This script is licensed under Creative Commons, CC4 BY-SA
//  http://creativecommons.org/licenses/by-sa/4.0/

key     Avatar;
default
{
    state_entry()
    {
        // This is where the avavatr is seated. Adjust to fit.
        llSitTarget(<0.9,0.5,-0.4>, ZERO_ROTATION);
    }
    
    changed(integer chg)
    {
        if (chg & CHANGED_LINK)
        {
            key id = llAvatarOnSitTarget();
            
            if (id != NULL_KEY)
            {
                // When the avatars sits on this prim, send a message to
                // the vehicle script to start.
                if (Avatar == NULL_KEY)
                {
                    llMessageLinked(LINK_SET, TRUE, "sit-target", id);
                    llRequestPermissions(id, PERMISSION_TRIGGER_ANIMATION);
                    Avatar = id;
                }
            }
            
            else
            {
                // Avatar stands, disengage.
                if (Avatar != NULL_KEY)
                {
                    llMessageLinked(LINK_SET, FALSE, "sit-target", id);
                    llRequestPermissions(llGetOwner(), 0);
                    Avatar = NULL_KEY;
                }
            }
        }
    }
    
    run_time_permissions(integer chg)
    {
        // Start the animation
        if (chg & PERMISSION_TRIGGER_ANIMATION)
        {
            llStopAnimation("sit");
            string  anim = llGetInventoryName(INVENTORY_ANIMATION, 0);
            if (anim != "") llStartAnimation(anim);
        }
    }
}