This is a fast and furious tut for making a scripted sailing vehicle for opensims. It works well on both single regions and megaregions, but excepting between subregions in a megaregion, it probably cannot cross from one sim to another at this time.
First, you will probably find that a prim larger than 10m in your vehicle build will prevent the script working. This is theoretically configurable per-region, but the safe bet is to use sub-10m prims in your vehicle build.
Additionally, while there is no fixed limit (that I'm aware of) to the number of prims you can use in a vehicle build, simpler is most likely better.
So for your boat you will need a hull with a mast. Our sail in this tut will consist of a single prim. First, make the boat hull. Remember that the boat portion of the script will go in the root prim, the boat will turn about the geometric center of this prim, and the captain's sit will be relative to it's position.
Having made such a hull (don't forget the mast!), you will now need to make the sail. The sail is a single prim, and is 'special' - it is special in that most of it is cut away to provide an edge (visible edge but geometric center) to pivot about. Following are the parameters for such a sail at it's maximum dimensions:
Code:
Size:
X: 0.01
Y: 10.00
Z: 10.00
Cut:
B: 0.610
E: 0.630
It is important to have the sail oriented with positive 'z' pointing up (arrows pointing 'up' with local coordinates for the sail displayed).
Did I mention you should have your hull and mast in a linkset yet? you should. Leave the sail unlinked for now, though.
The boat script goes in the root prim of the hull, and is as follows:
Code:
// Very simple vehicle script, mod for OpenSim & ODE & VEHICLE code
// By Kitto Flora September 2009
// remodified by Hiro Protagonist for basic sailing aquatic motorcraft
// September 2009
// grafted in from Owen Oyen's work implementing sail force simulations
float gmass;
float gleft;
float gright;
vector heading;
float gincrement;
integer g_leftrightflag;
integer gstart;
integer gchannel_num;
integer gtouch_toggle;
integer gtouchoff;
integer gsailposn = 0;
integer gsailleftright;
key g_agent;
rotation grot;
float gheel;
float gWindMag;
float gWindDir;
float gBowDir;
vector bow;
vector gwind;
float grel_angle;
float gboatspeed;
float gsailangle;
float thrust;
//
integer Private = 1; // Change to 1 to prevent others riding.
vector Sitpos = <-1.3,0.5,0.65>;
vector SitrotV = <0,-20,0>;
rotation Sitrot;
integer tt;
key oldagent;
key agent;
string sit_message = "Man the Helm"; //Sit message
string not_owner_message = "You are not the owner of this vehicle, buy a copy for 0$ and have your own to test in this sim. It will not work in other Open Sim Regions."; //Not owner message
setVehicle()
{
//boat
llSetVehicleType(VEHICLE_TYPE_BOAT);
llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.2);
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.80);
llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 0.10);
llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 0.10);
llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, 1.0);
llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 0.1);
llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE, 0.1);
llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 0.1);
llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <10.0, 2.0, 1000.0>);
llSetVehicleVectorParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE, <0.1, 0.1, 0.1>);
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.250);
llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 0.250);
}
Init()
{
Sound(0);
llSetBuoyancy(1);
llSetStatus(STATUS_PHYSICS, FALSE);
vector here = llGetPos();
float h = llWater(<0,0,0>) + 0.22;
vector rotv = llRot2Euler(llGetRot());
rotation rot = llEuler2Rot(<0,0,rotv.z>);
llSetPos(<here.x, here.y,h>);
llSetRot(rot);
Sitrot = llEuler2Rot(DEG_TO_RAD * SitrotV);
llSetVehicleType(VEHICLE_TYPE_NONE);
}
SetMaterial()
{
llSetPrimitiveParams([PRIM_MATERIAL, PRIM_MATERIAL_WOOD]);
}
Sound(integer n)
{
integer oldn;
if(n != oldn)
{
oldn = n;
if(n == 2)
{
// llLoopSound("RUNNING",1);
}
else if(n == 1)
{
// llLoopSound("IDLE",1);
}
else
{
// llStopSound();
}
}
}
default
{
state_entry()
{
Init();
llSetSitText(sit_message);
// forward-back,left-right,updown
llSitTarget(Sitpos, Sitrot);
// llStopSound();
}
on_rez(integer rn){
llResetScript();
}
changed(integer change)
{
if ((change & CHANGED_LINK) == CHANGED_LINK)
{
agent = llAvatarOnSitTarget();
if (agent != NULL_KEY)
{
if( (agent != llGetOwner()) && (Private == 1) )
{
llSay(0, not_owner_message);
llUnSit(agent);
}
else
{
//llTriggerSound("car_start",1);
oldagent = agent;
setVehicle();
SetMaterial();
llSetStatus(STATUS_PHYSICS, TRUE);
//Sensor is to make a crude Timer as TimerEvent fails on vehicles
llSensorRepeat("Non-Entity",NULL_KEY,PASSIVE,1.0, PI_BY_TWO,2);
llRequestPermissions(agent, PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS);
// Sound(1);
}
}
else
{
Init();
llReleaseControls();
// llStopSound();
}
}
}
touch_start(integer tn){
}
run_time_permissions(integer perm)
{
if (perm)
{
llStartAnimation("sit");
llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_DOWN | CONTROL_UP | CONTROL_RIGHT |
CONTROL_LEFT | CONTROL_ROT_RIGHT | CONTROL_ROT_LEFT, TRUE, FALSE);
llSetStatus(STATUS_PHYSICS, TRUE);
}
}
control(key id, integer level, integer edge)
{
vector angular_motor;
//get current speed
vector vel = llGetVel();
float speed = llVecMag(vel);
//sheet controls
if(level & CONTROL_FWD)
{
// pasted from Owen's original work implementing sailing physics
gsailposn = llAbs(gsailposn) + 1;
gsailposn *= gsailleftright;
gsailangle = gsailposn * DEG_TO_RAD;
//
}
if(level & CONTROL_BACK)
{
// pasted from Owen's original work implementing sailing physics
gsailposn = llAbs(gsailposn) - 1;
gsailposn *= gsailleftright;
gsailangle = gsailposn * DEG_TO_RAD;
//
}
// tiller controls
if(level & (CONTROL_RIGHT|CONTROL_ROT_RIGHT))
{
// pasted from Owen's original work implementing sailing physics
if (gboatspeed > 1)
{
// llLoopSound("boatunderway_short", 1.0);
gright = -0.1;
}
grot = llGetRot();
vector pres_heading = llRot2Euler(grot);
heading = pres_heading;
heading.x = 0; // tumble stop
heading.y = 0; // tumble stop
heading.z += (gright);
grot = llEuler2Rot(heading);
llSetRot(grot);
//
}
if(level & (CONTROL_LEFT|CONTROL_ROT_LEFT))
{
// pasted from Owen's original work implementing sailing physics
if (gboatspeed > 1)
{
// llLoopSound("boatunderway_short", 1.0);
gleft = 0.1;
}
grot = llGetRot();
vector pres_heading = llRot2Euler(grot);
heading = pres_heading;
heading.x = 0; // tumble stop
heading.y = 0; // tumble stop
heading.z += (gleft);
grot = llEuler2Rot(heading);
llSetRot(grot);
//
}
// pasted from Owen's original work implementing sailing physics
if (gsailposn > 90) gsailposn = 90; // set sail position
if (gsailposn < -90) gsailposn = -90; // set sail position
llMessageLinked(LINK_SET,0,(string)gsailposn,llGetOwner()); // say it to the sail
//
llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, angular_motor);
} //end control
//Sensor is to make a crude Timer as TimerEvent fails on vehicles
no_sensor()
{
llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <10.0, 2.0, 1000.0>);
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, <thrust,0,0>);
// pasted from Owen's original work implementing sailing physics
gwind = < 3,3,0>;
gWindMag = llVecMag(gwind);
gboatspeed = llVecMag(llGetVel());
bow = llRot2Euler(llGetRot()); // Euler angle description of boat's rotation
bow.z -= 1.57;
gBowDir = -bow.z;
if (gBowDir < -PI) gBowDir += TWO_PI;
// Wind direction derived from its velocity vector
// Wind speed ditto
gWindDir = llAtan2(gwind.y,gwind.x); // in Radians
if (gWindDir < 0)
gWindDir += TWO_PI;
grel_angle= gWindDir - gBowDir;
if (grel_angle > 0) // sail side flip
{
gsailleftright = -1;
}
else
{
gsailleftright = 1;
}
if (grel_angle > PI) grel_angle -= TWO_PI; // This handles traverse of the zero point of circle
if (grel_angle < -PI ) grel_angle += TWO_PI; // This does same for the negative case
float aaw = llSqrt( llPow((gWindMag * llCos (grel_angle) + gboatspeed), 2.) + llPow((gWindMag * llSin(grel_angle)),2.));
if (aaw == 0) {
aaw = .0001;
}
float awrel_angle = llAtan2((llSin(grel_angle)*gWindMag ) , (gboatspeed + llCos(grel_angle) * gWindMag));
if (awrel_angle > PI) awrel_angle -= TWO_PI; // This handles traverse of the zero point of circle
if (awrel_angle < -PI ) awrel_angle += TWO_PI; // This does same for the negative case
thrust = llFabs(llCos(awrel_angle + gsailangle) * aaw *0.85); // Primary propulsion
gheel += llSin(awrel_angle) * aaw * 3 * DEG_TO_RAD;
if (gheel > 40)
gheel = 40;
heading.x = gheel; // tumble stop
heading.y = 0; // tumble stop
heading.z += (gleft);
grot = llEuler2Rot(heading);
llSetText(" Speed: "+(string)gboatspeed + " m/s heading: " + (string)gBowDir + " wind: " + (string)gWindDir + " sail trim: " + (string)gsailposn,<1,1,1>,1.0);
//
}
} //end default
You will likely need to tweak the vertical position of the root prim to get the boat to sit properly in the water.
The sail script goes in the sail. It is as shown:
Code:
default
{
link_message(integer sender, integer parameter, string message, key uuid)
{
if(uuid == llGetOwner())
{
float sheet=(float) message;
llSetLocalRot(llEuler2Rot(DEG_TO_RAD*<0,10,((-1*sheet)+90)>));
}
}
}
ProTip: you can also slip the sail script into a similarly crafted boomHaving gotten the sail script into the sail, you should now position it relative to the mast and link it with the rest of the boat. Be sure to select the sail first in order to preserve the preexisting root prim.
Notes:
There is only a steady debug wind blowing out of the west. This could easily be changed to a variety of other methods.
The sail does not douse when the Captain stands. It would probably be good if it did.
The boat will sail right up onto and over land; this should probably be addressed.
All in all, this is a very simple working sailboat implementation for OpenSims - it is being offered to those who would form sailing and boating communities as some 'small stuff' in the tie that binds such, to do with as they will.
Have Fun!
Cheers
Hiro Protagonist/James Stallings II
ODE LSL Vehicle support for opensim by Kitto Flora
Boat Scripting by Hiro Protagonist aka James Stallings (derived from other work by Kitto Flora, Owen Oyen, and Hiro Protagonist)
Tutorial courtesy of James Stallings and SimHost.com