De DigiWiki.
// --------------------------------------------------------
// SAILING SCRIPT VERSION 1-37 BBK EDITION
// --------------------------------------------------------
// BWind Sailing Engine Release 1-37
// by Becca Moulliez - RELEASE June 2010 - GNU/GPL
// SAIL + APPARENT WIND
// refer to boat name for release :
// BOAT RELEASE-WIND ENGINE VERSION-LATEST UPDATE VERSION
// i.e : 1-1-1
// script based upon Kanker Greenacre's Flying Tako
//------------------------------------------------------
// Please do not remove this header
// Permitted Free Uses
// - allowed to use in your personal boats.
// - allowed to use in boats you wish to sell or give away in second life.
// - allowed to modify any part to your particular needs
// Not Allowed Uses
// - not allowed to re-package and give away, or sell this script.
// - not allowed to change a few lines of code then call this your own work
// - not allowed to take this to another platform or any use other than Second Life sailing without the express permission from Becca Moulliez
// ======= End Header =========
// ----------------------------------------- START PROGRAMME ---
///////////////////////////////////////////////////////////////////////
// GLOBAL BOAT SETTINGS /////////////////////////////////////////////
// you don't want to modify these settings unless specifically noted //////
/////////////////////////////////////////////////////////////////////
//version settings
string boatName="Becky's BBK "; // Rename this according to your needs...
string versionNumber="1-37-x"; // Release reference
//script module flags
integer CONTROLS_MODULE=1;
integer SAIL_MODULE=2;
//environment
vector wind;
float windAngle;
float absWindAngle;
float seaLevel;
//reused math variables
vector eulerRot;
vector currEuler;
rotation quatRot;
rotation currRot;
//boat variables
float zRotAngle;
vector fwdVec;
vector upVec;
vector leftVec;
float compass;
//heeling variables
float heelAngle;
float heelTorque;
float heelAdd;
//linear motion variables
float currSpeed;
vector groundSpeed=ZERO_VECTOR;
float spdFactor=0.0;
float leeway;
//angular motion variables
float rotSpeed;
float rotDelta;
vector eulerTurnLeft;
vector eulerTurnRight;
rotation quatTurnLeft=ZERO_ROTATION;
rotation quatTurnRight=ZERO_ROTATION;
//sail variables
integer sailingAngle;
integer currBoomAngle=0;
integer delta;
integer incr;
float optBoomAngle;
float trimFactor;
//spinnaker variables
integer spinAngle;
integer spindelta;
integer currSpinAngle=0;
integer SPIN_UP=FALSE;
integer CurJib;
integer CurSpin;
string Tack;
//performance constants - Standard defaults
float timerFreq=1.0; //timer frequency, seconds (original paramer 1.5; check this if gets laggy)
integer sheetAngle=5; //initial sheet angle setting
float maxWindSpeed=14.0; //used for heeling calculation (better leave this unchanged)
//miscellaneous settings
key owner; //boat owner
key avatar; //avatar sitting at the helm
integer lock=FALSE;
integer ownerVersion=TRUE;
integer SAIL_UP=FALSE;
integer permSet=FALSE;
integer HUD_ON=TRUE;
string idStr;
integer numTouches=0;
integer sailing=TRUE;
float mpsToKts=1.944; //Metres per Second to Knots conversion
float convert=1.944;
string units=" Kts.";
integer showKnots=TRUE; //Yes we show speed in Knots...
float time;
float offset;
float theta;
integer msgTypeModeChange=40001;
integer modeBWind=0;
string helpString;
string visualAid;
vector hudcolour;
string currentString;
integer ADV_HUD=FALSE; //Advanced HUD off by default; set to TRUE to set on...
//linked parts - childprims declarations; the rootscript will send commands to childs when running...
//to work properly relevant prims MUST be renamed following the scheme below
integer JIB;
integer SAIL;
integer BOOM;
integer HUD;
integer CREWMAN; //this refers to the Crew Poseball in the cockpit...
integer POLE; //NEW for Spinnaker
integer SPINNAKER; //NEW for Sinnaker
//general sailing parameters
float ironsAngle=31; //this is as close as the boat can sail to the wind
float slowingFactor=0.7; //speed drops by this factor every timerFreq seconds if sailing into the wind
float leewayTweak=1.50; //scales leeway (0=no leeway)
float rotTweak=0.8; //scales boat turning rate
float speedTweak=1.0; //DON'T touch this !!!!!!!!
//Apparent Wind parameters
vector tmpwind;
float truewindDir;
float truewindSpeed;
float truewindAngle;
float appwindSpeed;
float appwindAngle;
// ------------------------- END GLOBAL BOAT SETTINGS ---
///////////////////////////////////////////////////////
// BWIND DEFAULT PRESET DECLARATION //////////////////
// modify this section as per 15 Knots Wind Preset //
////////////////////////////////////////////////////
//The boat will sail a 15 Knots East BWind by default... following parameters apply
float windDir=0; //BWind Wind direction
string windRose="East "; //BWind Preset declaration for 15 Knots Wind
string windType="15 Knots"; //BWind Preset declaration for 15 Knots Wind
//primary parameters
float windSpeed=7.75; // Don't touch this... 15 Knots Speed
float maxSpeed=5.5; // Edit this according to your needs... this is the actual Speed with a 15 Knots Wind
float heelTweak=0.85; // Edit this according to your needs... this is the actual Heel for a 15 Knots Wind
//Note : you MUST check the 15 Knots BWind Preset below in the Script (line 829); The values set here MUST be the same...
// --------------------------- END DEFAULT PRESET DECLARATION ---
///////////////////////////////////////////////////
// GLOBAL BOAT EQUATIONS AND FUNCTIONS ///////
// you don't want to modify these settings //////
////////////////////////////////////////////////
//Following Equations and Functions will affect GLOBAL Boat's behavior -- I strongly recommend you will NOT edit unless you know what you are doing...
// BWind Basic Boat Behaviour - LSL Angle Calculation
integer realAngle2LslAngle(integer realAngle) {
integer lslAngle= (realAngle-90)*-1;
while(lslAngle>=360) lslAngle-=360;
while(lslAngle<0) lslAngle+=360;
return lslAngle;
}
// BWind Basic Boat Behaviour - calculate wind angle
//TRUE WIND - True Wind has been left as a separate routine in case you dont want your boat to support Apparent Wind
calcTrueWindAngle() {
currRot=llGetRot();
currEuler=llRot2Euler(currRot);
zRotAngle=currEuler.z;//boat heading
leftVec=llRot2Left(currRot);
truewindAngle=windDir-zRotAngle;
while (truewindAngle>PI) truewindAngle-=TWO_PI; //bw -PI and PI
while (truewindAngle<-PI) truewindAngle+=TWO_PI; //bw -PI and PI
if ((truewindAngle) < 0) {
truewindAngle = truewindAngle * -1;
}
}
//APPARENT WIND
calcAppWindAngle() {
currRot=llGetRot();
currEuler=llRot2Euler(currRot);
zRotAngle=currEuler.z; //boat heading
leftVec=llRot2Left(currRot);
windAngle=windDir-zRotAngle;
while (windAngle>PI) windAngle-=TWO_PI; //bw -PI and PI
while (windAngle<-PI) windAngle+=TWO_PI; //bw -PI and PI
vector boatMovement=<currSpeed*llCos(currEuler.z),currSpeed*llSin(currEuler.z),0>;
tmpwind=wind+boatMovement;
float spd=llVecMag(llGetVel());
appwindAngle=llAtan2(windSpeed*llSin(windAngle),(windSpeed*llCos(windAngle)+spd));
while (appwindAngle>PI) spd=-spd;
while (appwindAngle<-PI) spd=-spd;
appwindSpeed=spd*llCos(llFabs(windAngle))-windSpeed*llCos(windAngle);
if ((appwindSpeed) < 0) {
appwindSpeed = appwindSpeed * -1;
}
if ((appwindSpeed) > 3) {
appwindSpeed = appwindSpeed -(TWO_PI-1.0);
}
if ((appwindAngle) < 0) {
appwindAngle = appwindAngle * -1;
}
}
// BWind Basic Boat Behaviour - calculate heel angle based on wind and sail settings
calcHeelAngle() {
heelAngle=llAsin(leftVec.z);
if (SAIL_UP)
if (llFabs(windAngle+sailingAngle)>3*DEG_TO_RAD)
heelTorque=SAIL_UP*llSin(windAngle)*llCos(heelAngle)*PI_BY_TWO*(windSpeed/maxWindSpeed)*llCos(sailingAngle*DEG_TO_RAD)*heelTweak;
else heelTorque=0;
else heelTorque=0;
heelAdd=heelTorque-heelAngle;
eulerRot=<heelAdd,0,0>;
quatRot=llEuler2Rot(eulerRot);
}
// BWind Basic Boat Behaviour - calculate angle of sail (or jib) based on sheet setting and the wind
calcBoomDelta() {
if (sheetAngle<=0) sheetAngle=5; //never let the actual sheetangle be less than 5°
if (sheetAngle>=79) sheetAngle=79; //never let the actual sheetangle be more than 79°
sailingAngle=sheetAngle;
if (sailingAngle>llFabs(windAngle*RAD_TO_DEG)) sailingAngle=llRound(llFabs(windAngle*RAD_TO_DEG));
if (windAngle<0) sailingAngle*=-1;
delta=sailingAngle-currBoomAngle;
currBoomAngle=sailingAngle;
if (currBoomAngle < 0) Tack="Starb'd";
if (currBoomAngle > 0) Tack="Port";
currBoomAngle=sailingAngle;
llMessageLinked(SAIL,delta,"",NULL_KEY);//tell sail to rotate by delta
llMessageLinked(JIB,delta,"",NULL_KEY);//tell jib to rotate by delta
llMessageLinked(BOOM,delta,"",NULL_KEY);//tell boom to rotate by delta
}
// BWind Basic Boat Behaviour - calculate boat speed
calcSpeed() {
groundSpeed=llGetVel();
absWindAngle=llFabs(windAngle);
if (llFabs(absWindAngle*RAD_TO_DEG-llFabs(sailingAngle))<10) trimFactor=0;
else {
optBoomAngle=0.5*absWindAngle*RAD_TO_DEG;
trimFactor=(90.-llFabs(optBoomAngle-llFabs(sailingAngle)))/90.;
}
//(old)if (absWindAngle<ironsAngle*DEG_TO_RAD) currSpeed*=slowingFactor;
if (appwindAngle<ironsAngle*DEG_TO_RAD) currSpeed*=slowingFactor;
else {
if (SAIL_UP) {
//currSpeed=speedTweak*(llCos(windAngle/2.)+0.5)*windSpeed*trimFactor;
//if (currSpeed>maxSpeed) currSpeed=maxSpeed;
currSpeed=speedTweak*(llSin(llFabs(windAngle)/2)+llCos(llFabs(windAngle)/2.75) - 0.75)*windSpeed*trimFactor; // mod donated by Eta Carver
}
else currSpeed*=0.8;
}
}
// BWind Basic Boat Behaviour - calculate leeway (lateral drift) due to wind
calcLeeway() {
leeway=SAIL_UP*-llSin(appwindAngle)*llSin(heelAngle)*windSpeed*leewayTweak;
//BUG found by Balduin Aabye - ty :))
}
// BWind Basic Boat Behaviour - calculate turning rate based on current speed
calcTurnRate() {
spdFactor=llVecMag(groundSpeed)/(maxSpeed);
rotSpeed=0.5+(spdFactor)/2.0;
}
// String Conversions - automatically detect link nums for each named part - based upon original Tako parts names
getLinkNums() {
integer i;
integer linkcount=llGetNumberOfPrims();
for (i=1;i<=linkcount;++i) {
string str=llGetLinkName(i);
if (str=="jib") JIB=i;
if (str=="sail") SAIL=i;
if (str=="boom") BOOM=i;
if (str=="pole") POLE=i;
if (str=="spinnaker") SPINNAKER=i;
if (str=="hud") HUD=i;
if (str=="crewman") CREWMAN=i;
}
}
// RAISE ROUTINE - raise sail: start timer
raiseSail() {
SAIL_UP=TRUE;
llOwnerSay("Ready to sail... ");
llMessageLinked(SAIL,1002,"",NULL_KEY); // raise sail
llMessageLinked(JIB,1002,"",NULL_KEY); // raise jib
llMessageLinked(BOOM,1002,"",NULL_KEY); // move boom
llMessageLinked(CREWMAN,1000,"",NULL_KEY); // hide crewman poseball
llSetTimerEvent(timerFreq);
llLoopSound("sailing", 1.0);
}
// LOWER ROUTINE - lower sail but leave physics on
lowerSail() {
llOwnerSay("Lowering sails... ");
llMessageLinked(SAIL,1000,"",NULL_KEY);//lower sail w/ reset
llMessageLinked(JIB,1000,"",NULL_KEY);//lower jib w/ reset
llMessageLinked(BOOM,1000,"",NULL_KEY);//stop boom w/ reset
llMessageLinked(POLE,1000,"",NULL_KEY);//stop pole w/ reset
llMessageLinked(SPINNAKER,1000,"",NULL_KEY); // drop and reset spinnaker
sailingAngle = 0;
llStopSound();
llMessageLinked(LINK_ALL_CHILDREN , 0, "stop", NULL_KEY); // stop all children-linked activities
currBoomAngle=0;
sheetAngle=5;
SAIL_UP=FALSE;
llSetObjectDesc ("BWind Builder's Kit - Keelboat"); //This sets your boat's description string when lowering... useful when you add a WWC Wind routine
}
//SPINNAKER ROUTINE
HoistSpin(){
if ((sheetAngle > 50)) {
llOwnerSay("Hoisting Spinnaker");
llMessageLinked(SPINNAKER,1002,"",NULL_KEY); //hoist spinnaker
if (-sailingAngle > 0) llMessageLinked(POLE,1002,"",NULL_KEY);
if (-sailingAngle < 0) llMessageLinked(POLE,1004,"",NULL_KEY);
llMessageLinked(JIB,1001,"",NULL_KEY); //lower jib
SPIN_UP=TRUE;
CurJib=0;
}
if ((sheetAngle < 50)) {
llOwnerSay("Sheet too close to Hoist Spin");
}
}
DropSpin(){
llOwnerSay("Dropping Spinnaker");
llMessageLinked(SPINNAKER,1000,"",NULL_KEY); //drop spinnaker/W reset
llMessageLinked(POLE,1000,"",NULL_KEY); // hide & reset pole
llMessageLinked(JIB,1002,"",NULL_KEY); //raise jib
CurSpin=0;
CurJib=0;
currSpinAngle=0;
spinAngle=0;
SPIN_UP=FALSE;
}
TrimSpinPlus() {
if (CurSpin < 37) {
llMessageLinked(SPINNAKER,(delta+3),"",NULL_KEY); //trim spinnaker +
llMessageLinked(POLE,(delta+3),"",NULL_KEY); // hide pole
CurSpin=CurSpin+3;
}
}
TrimSpinMinus() {
if (CurSpin > -37) {
llMessageLinked(SPINNAKER,(delta-3),"",NULL_KEY); //trim spinnaker -
llMessageLinked(POLE,(delta-3),"",NULL_KEY); // hide pole
CurSpin=CurSpin-3;
}
}
// VEHICLE PHYSICS PARAMETERS - set initial vehicle parameters
// WARNING !!!! - Following physics parameters should NOT be edited unless you REALLY KNOW what you are doing....
setVehicleParams() {
//vehicle flags
llSetVehicleType (VEHICLE_TYPE_BOAT);
llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME,ZERO_ROTATION); // ZERO_ROTATION = <0.0,0.0,0.0,1.0> you may wish to edit this for fun
llSetVehicleFlags (VEHICLE_FLAG_NO_DEFLECTION_UP|VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT|VEHICLE_FLAG_LIMIT_MOTOR_UP );
//linear motion
llSetVehicleVectorParam (VEHICLE_LINEAR_FRICTION_TIMESCALE,<50.0,2.0,0.5>);;
llSetVehicleVectorParam (VEHICLE_LINEAR_MOTOR_DIRECTION,ZERO_VECTOR);
llSetVehicleFloatParam (VEHICLE_LINEAR_MOTOR_TIMESCALE,10.0);
llSetVehicleFloatParam (VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE,60);
llSetVehicleFloatParam (VEHICLE_LINEAR_DEFLECTION_EFFICIENCY,0.85);
llSetVehicleFloatParam (VEHICLE_LINEAR_DEFLECTION_TIMESCALE,1.0);
//angular motion
llSetVehicleVectorParam (VEHICLE_ANGULAR_FRICTION_TIMESCALE,<5,0.1,0.1>);
llSetVehicleVectorParam (VEHICLE_ANGULAR_MOTOR_DIRECTION,ZERO_VECTOR);
llSetVehicleFloatParam (VEHICLE_ANGULAR_MOTOR_TIMESCALE,0.1);
llSetVehicleFloatParam (VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE,3);
llSetVehicleFloatParam (VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY,1.0);
llSetVehicleFloatParam (VEHICLE_ANGULAR_DEFLECTION_TIMESCALE,1.0);//default 1.0 -- reduce to have more lateral drift (like 0.3 - 0.5)
//vertical attractor
llSetVehicleFloatParam (VEHICLE_VERTICAL_ATTRACTION_TIMESCALE,3.0);
llSetVehicleFloatParam (VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY,0.8);
//banking
llSetVehicleFloatParam (VEHICLE_BANKING_EFFICIENCY,0.0);
llSetVehicleFloatParam (VEHICLE_BANKING_MIX,1.0);
llSetVehicleFloatParam (VEHICLE_BANKING_TIMESCALE,1.2);
//vertical control
llSetVehicleFloatParam (VEHICLE_HOVER_HEIGHT,seaLevel);
llSetVehicleFloatParam (VEHICLE_HOVER_EFFICIENCY,2.0);
llSetVehicleFloatParam (VEHICLE_HOVER_TIMESCALE,1.0);
llSetVehicleFloatParam (VEHICLE_BUOYANCY,1.0);
}
//MASTMAN AND CREW CAMERA SETUP - set camera position for third person view
setCamera() {
llSetCameraEyeOffset(<-5.4,0.0,1.4>); //Here you may set your Sailing Camera EYE Offset
llSetCameraAtOffset(<3.0,0.0,1.0>); //Here you may set your Sailing Camera actual position
}
//REZZING INITIAL POSITION - figure out where to put boat when it is rezzed
// The boat will float at a 20.100 level... Edit and raise/lower the rootprim to set your waterline... DON'T mod THIS !
setInitialPosition() {
vector pos=llGetPos();
float groundHeight=llGround(ZERO_VECTOR);
float waterHeight = llWater(ZERO_VECTOR);
seaLevel=llWater(ZERO_VECTOR);
upright();
//if over water, set boat height to sealevel + 0.1m; this is the standard default water level
if (groundHeight <= waterHeight) {
pos.z = waterHeight + 0.1; //fixed by Balduin Aabye (originally 0.12)
while (llVecDist(llGetPos(),pos)>.001) llSetPos(pos);
}
}
//SIT TARGET - REFER TO pose_s SCRIPT FOR MASTMAN AND CREW SET - set sit target for helmsperson
//this function is now being disabled - there is an external pose script to set this...
setSitTarget() {
llSetSitText("Sail !");
llSetText("",ZERO_VECTOR,1.0);
}
//BOAT UPRIGHT POSITION - force boat upright
upright() {
currRot=llGetRot();
currEuler=llRot2Euler(currRot);
leftVec=llRot2Left(currRot);
heelAngle=llAsin(leftVec.z);
eulerRot=<-heelAngle,0,0>;
quatRot=llEuler2Rot(eulerRot);
llRotLookAt(quatRot*currRot,0.2,0.2);
}
//MOORING FUNCTION - what happens when your boat moors...
moor() {
llMessageLinked(LINK_THIS,SAIL_MODULE,"moor",NULL_KEY);
{
llSetTimerEvent(timerFreq);
sailing=TRUE;
}
llOwnerSay("Mooring.");
upright();
llReleaseControls();
llSetStatus(STATUS_PHYSICS,TRUE);
llSetTimerEvent(0);
currSpeed=0;
}
//GENERAL BOAT STARTUP - reset stuff - this resets the script to defaults... you may call this function as needed...
startup() {
owner=llGetOwner();
llSetStatus(STATUS_ROTATE_X | STATUS_ROTATE_Z | STATUS_ROTATE_Y,TRUE);
llSetStatus(STATUS_PHYSICS,FALSE);
llSetStatus(STATUS_PHANTOM,FALSE);
llSetStatus(STATUS_BLOCK_GRAB,TRUE);
llSetTimerEvent(0);
setInitialPosition();
setVehicleParams();
setSitTarget();
getLinkNums();
llMessageLinked(SAIL,1000,"",NULL_KEY); //reset MAINSAIL
llMessageLinked(JIB,1000,"",NULL_KEY); //reset JIB
llMessageLinked(BOOM,1000,"",NULL_KEY); //reset BOOM
llMessageLinked(CREWMAN,1001,"",NULL_KEY); // show crewman poseball
llMessageLinked(POLE,1000,"",NULL_KEY); // hide & reset pole
llMessageLinked(SPINNAKER,1000,"",NULL_KEY); //reset SPINNAKER
setCamera(); //apply Camera default setting
currSpeed=0;
llListen(0,"",owner,""); //listen to boat owner only...
llOwnerSay("Ready.");
llMessageLinked(LINK_ALL_CHILDREN , 0, "stop", NULL_KEY);
}
// ------------------------------------- END GLOBAL BOAT EQUATIONS AND FUNCTIONS---
///////////////////////////////////////////////////
// HUD SETTINGS //////////////////////////////////
// you don't want to modify these settings //////
////////////////////////////////////////////////
// UPDATE HUD - Main HUD routine and colour management
updateHUD() {
string dataString;
float compass=PI_BY_TWO-zRotAngle;
float effcoeff;
string rgn = llGetRegionName();
string blank = " ";
float efficiency;
string ratio;
float derivatesheetAngle=sheetAngle;
//CALCULATE SPINNAKER EFFECTS ON EFFICIENCY
//Spinnaker tweak weighs 1/3 of global sheet
if (Tack=="Starb'd") {
derivatesheetAngle+=(CurSpin/3); //Starboards Spinnaker Tweak
}
if (Tack=="Port") {
derivatesheetAngle-=(CurSpin/3); //Port Spinnaker Tweak
}
//ACTUAL EFFICIENCY CALCULATION including spinnaker tweaks
efficiency = appwindAngle*RAD_TO_DEG/derivatesheetAngle;
if ((efficiency) < 0) {
efficiency = efficiency * -1;
}
compass=PI_BY_TWO-zRotAngle;
while (compass<0) compass+=TWO_PI;
dataString = " "; //clean hud message on startup
currentString = " "; //clean hud storage on startup
//HUD COMPASS CONVERSION
float hudcompass =((integer)(compass*RAD_TO_DEG));
string huddirection;
if ((hudcompass) <= 360) {
huddirection="North";
}
if ((hudcompass) <= 315) {
huddirection="Northwest";
}
if ((hudcompass) <= 275) {
huddirection="West";
}
if ((hudcompass) <= 230) {
huddirection="Southwest";
}
if ((hudcompass) <= 185) {
huddirection="South";
}
if ((hudcompass) <= 140) {
huddirection="SouthEeast";
}
if ((hudcompass) <= 95) {
huddirection="East";
}
if ((hudcompass) <= 50) {
huddirection="Northeast";
}
if ((hudcompass) <= 5) {
huddirection="North";
}
// STANDARD HUD STRING CONSTRUCTION ROUTINE
if (ADV_HUD==FALSE) {
// efficiency positive convert and ratio calculation
if ((efficiency) < 0) {
efficiency = efficiency * -1;
}
ratio = llGetSubString ((string)efficiency, 0, 3);
vector hudcolour=<1.0,1.0,1.0>; //default hudcolour
dataString+="-> "+huddirection+" "; //add a direction string
dataString+="( " +(string)((integer)(compass*RAD_TO_DEG))+"° )\n "; //add compass degrees
dataString+="| "+visualAid+" | Speed "+llGetSubString((string)(llVecMag(groundSpeed*convert)),0,3)+units+"\n"; //add boat speed
dataString+=(windType)+" "+(windRose)+"BWind\n"; //add BWind wind preset
//trailing blanks
dataString+=blank+"\n";
dataString+=blank+"\n";
}
// ADVANCED HUD STRING CONSTRUCTION ROUTINE
if (ADV_HUD==TRUE) {
// efficiency positive convert
if ((efficiency) < 0) {
efficiency = efficiency * -1;
}
vector hudcolour=<1.0,1.0,1.0>; //default hudcolour
dataString+="-> "+huddirection+" "; //add a direction string
dataString+="( " +(string)((integer)(compass*RAD_TO_DEG))+"° )\n "; //add compass degrees
dataString+="| "+visualAid+" | Speed "+llGetSubString((string)(llVecMag(groundSpeed*convert)),0,3)+units+" - "; //add boat speed
dataString+=(windType)+" "+(windRose)+"BWind\n"; //add BWind wind preset
dataString+="True Wind Angle " +(string)((integer)(truewindAngle*RAD_TO_DEG))+"° - "; //add True Wind
dataString+="App. Wind Angle " +(string)((integer)(appwindAngle*RAD_TO_DEG))+"°\n"; //add Apparent Wind
ratio = llGetSubString ((string)efficiency, 0, 3); //display efficiency conversion
dataString+="Wind/Sheet ratio " +ratio+ " - Sheet Angle "+((string)(sheetAngle))+"°\n"; //add Efficiency and Sheet Angle
//trailing blanks
dataString+=blank+"\n";
dataString+=blank+"\n";
}
//---------- END HUD CONSTRUCTION ROUTINE
// HUD Colour Manager - This activates the HUD's virtual telltale function and affects ACTUAL BOAT'S SPEED WHILE TRIMMING
// THIS ROUTINE IS VITAL FOR YOUR BOAT PERFORMANCE... THIS SETS THE BOAT'S SPEED WHEN YOU TRIM SAILS
// too tight - cyan - this is good as it is...
if ((efficiency) < 25.0) {
hudcolour=<0.0,1.0,1.0>;
dataString==currentString; //update hud message on activation
visualAid="<>"; //help symbol for colour visual impaired - immediate feel of TOO TIGHT
speedTweak=0.3; //MODIFY THIS VALUE ACCORDING TO YOUR NEEDS... TWEAKS BOAT'S SPEED
}
// optimal - green - INCREASE THIS (never over 3) for easier apparent wind sailing
// LOWER THIS (never below 2) for a more demanding trim activity
if ((efficiency) < 2.5) {
hudcolour=<0.0,1.0,0.0>;
dataString==currentString; //update hud message on activation
visualAid="="; //help symbol for colour visual impaired - immediate feel of OPTIMAL
speedTweak=1.0; //MODIFY THIS VALUE ACCORDING TO YOUR NEEDS... TWEAKS BOAT'S SPEED
//SPINNAKER Boost/De-Boost
if (SPIN_UP==TRUE) {
if ((sheetAngle < 65)) {
speedTweak+=0.3;
}
if ((sheetAngle > 65)) {
speedTweak+=0.0;
}
if ((sheetAngle < 50)) {
speedTweak-=1.0; //original 0.5
if (speedTweak < 0) speedTweak = 0.00001;
}
}
}
// off optimal - yellow - here the sheet is too loose, but your boat will still sail quite well
if ((efficiency) < 1.7) {
hudcolour=<1.0,1.0,0.0>;
dataString==currentString; //update hud message on activation
visualAid="><"; //help symbol for colour visual impaired - immediate feel of TOO LOOSE
speedTweak=0.7; //MODIFY THIS VALUE ACCORDING TO YOUR NEEDS... TWEAKS BOAT'S SPEED
}
// too loose - red - oh yes this is very bad... the boat will almost stop sailing :)
if ((efficiency) < 1.2) {
hudcolour=<1.0,0.0,0.0>;
dataString==currentString; //update hud message on activation
visualAid=">><<"; //help symbol for colour visual impaired - immediate feel of WAY TOO LOOSE
speedTweak=0.3; //MODIFY THIS VALUE ACCORDING TO YOUR NEEDS... TWEAKS BOAT'S SPEED
}
//display hud string - HERE WE START HUD
llSetText(dataString,hudcolour,1.0);
currentString==dataString; //save current HUD message
//Tacking animation management - HERE YOU CAN DECTIVATE THE TACKING ANIMATION - JUST COMMENT (//) THE FOLLOWING LINES
if (-sailingAngle > 0) {
if ((llGetPermissions() & PERMISSION_TRIGGER_ANIMATION) && llGetAgentSize(llGetPermissionsKey()) != ZERO_VECTOR) {
llStopAnimation("bbk_helmsman");
llStartAnimation ("bbk_helmstack");
}
}
if (-sailingAngle < 0) {
if ((llGetPermissions() & PERMISSION_TRIGGER_ANIMATION) && llGetAgentSize(llGetPermissionsKey()) != ZERO_VECTOR) {
llStopAnimation("bbk_helmstack");
llStartAnimation ("bbk_helmsman");
}
}
}
// ---------------------------- END HUD SETTINGS ---
//////////////////////////////////////////////////////////////////////////////////////////////
// SCRIPT TRAPPING ROUTINE - state default //////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
default {
//get boat parts status
state_entry() {
getLinkNums();
llSetText("",ZERO_VECTOR,1.0);
startup();
llSetStatus(STATUS_BLOCK_GRAB,TRUE);
}
//reset boat
on_rez(integer param) {
llResetScript();
}
//OWNER CHECK AND STATUS
changed(integer change) {
avatar=llAvatarOnSitTarget();
if (change & CHANGED_LINK) {
if (avatar==NULL_KEY) {
if (!(llGetAgentInfo(owner) & AGENT_ON_OBJECT)) {
if (SAIL_UP) lowerSail();
if (permSet) llReleaseControls();
permSet=FALSE;
llMessageLinked(LINK_SET, 70400, "", NULL_KEY);
llResetScript();
}
}
else {
if (ownerVersion && avatar!=owner) llWhisper(0,"Only the owner can operate this boat.");
else if ((llGetAgentInfo(owner) & AGENT_ON_OBJECT)) {
llWhisper(0,"Say raise to start sailing, help for sailing commands...");
llWhisper(0,"BWind System defaults to East Wind, 15 Knots...");
if (llAvatarOnSitTarget()==owner) llRequestPermissions(owner,PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION);
}
}
}
}
//BOAT CONTROLS SETUP
run_time_permissions(integer perms) {
if (perms & (PERMISSION_TAKE_CONTROLS)) {
llTakeControls(CONTROL_RIGHT | CONTROL_LEFT | CONTROL_ROT_RIGHT |
CONTROL_ROT_LEFT | CONTROL_FWD | CONTROL_BACK | CONTROL_DOWN | CONTROL_UP,TRUE,FALSE);
permSet=TRUE;
if (permSet) llStartAnimation("bbk_helmsman");
llMessageLinked(LINK_SET, 70400, "", avatar);
}
}
// ------------------------ END STATE DEFAULT DECLARATIONS ---
////////////////////////////////////////////////////////////////////
// MAIN BOAT LISTENER /////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// YOU BETTER NEVER EDIT THE FOLLWING LINES UNLESS YOU KNOW WHAT TO DO :)
listen(integer channel, string name, key id, string msg) {
if (channel==0) {
if (owner==id & llAvatarOnSitTarget()==owner) {
if (llGetAgentInfo(owner) & AGENT_ON_OBJECT) {
if (llGetSubString(msg,0,4)=="sheet") {
incr=(integer)llDeleteSubString(msg,0,4);
sheetAngle+=incr;
if (sheetAngle>90) sheetAngle=90;
}
//MESSAGE raise - hey we DO want to sail huh?
else if (msg=="raise") {
llMessageLinked(LINK_ALL_CHILDREN , 0, "start", NULL_KEY);
sailing=TRUE;
if (!permSet) llRequestPermissions(owner,PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION);
permSet=TRUE;
llSetStatus(STATUS_PHYSICS,TRUE);
raiseSail();
llSetTimerEvent(timerFreq);
}
// SPINNAKER HOIST/DROP
else if (msg=="spin" && SPIN_UP) DropSpin();
else if (msg=="spin" && !SPIN_UP) HoistSpin();
//SPINNAKER TRIM
else if (msg=="spin+") TrimSpinPlus();
else if (msg=="spin-") TrimSpinMinus();
//GYBE POLE
else if (msg=="gybe") {
if (SPIN_UP==TRUE) {
llMessageLinked(SPINNAKER,1004,"",NULL_KEY); //hoist spinnaker
if (-sailingAngle > 0) llMessageLinked(POLE,1003,"",NULL_KEY);
if (-sailingAngle < 0) llMessageLinked(POLE,1004,"",NULL_KEY);
CurSpin=0;
}
}
//MESSAGE lower - okay now we want physics ON but no sailing...
else if (msg=="lower") lowerSail();
else if (msg=="moor") {
llMessageLinked(LINK_ALL_CHILDREN , 0, "stop", NULL_KEY);
moor();
llSetTimerEvent(0);
if (SAIL_UP) lowerSail();
llResetScript();
}
//BOAT ID Setter
else if (llGetSubString(msg,0,1)=="id") {
if (llGetSubString(msg,3,-1)!="off") {
idStr=llGetSubString(msg,3,-1);
string tmp=boatName+" #"+idStr;
llSetObjectName(tmp);
llWhisper(0,"New Boat ID :"+tmp);
}
}
//START bbk_helmsman ANIMATION
else if (llGetSubString(msg,0,3)=="anim") {
if (llGetSubString(msg,5,-1)=="off") {
}
else if (llGetSubString(msg,5,-1)=="on") {
if (permSet) llStartAnimation("bbk_helmsman"); // change thise pose/animation name if needed...
}
}
}
}
//-------------------------------- END MAIN BOAT LISTENER ---
///////////////////////////////////////////////////////////////
// BWind Engine - WIND DIRECTION PRESETS /////////////////////
//////////////////////////////////////////////////////////////
//wind direction in degrees - North = 0°
//you don't want to modify the following presets unless you want to add other BWind directions
if (lock==FALSE) {
if (msg=="n") {
windRose ="North ";
windDir=(90*DEG_TO_RAD);
llWhisper(0,"BWind now blowing from North");
}
if (msg=="nw") {
windRose ="Northwest ";
windDir=(135*DEG_TO_RAD);
llWhisper(0,"BWind now blowing from Northwest");
}
if (msg=="ne") {
windRose ="Northeast ";
windDir=(45*DEG_TO_RAD);
llWhisper(0,"BWind now blowing from Northeast");
}
if (msg=="e") {
windRose ="East ";
windDir=(0*DEG_TO_RAD);
llWhisper(0,"BWind now blowing from East");
}
if (msg=="s") {
windRose ="South ";
windDir=(270*DEG_TO_RAD);
llWhisper(0,"BWind now blowing from South");
}
if (msg=="sw") {
windRose ="Southwest ";
windDir=(225*DEG_TO_RAD);
llWhisper(0,"BWind now blowing from Southwest");
}
if (msg=="se") {
windRose ="Southeast ";
windDir=(315*DEG_TO_RAD);
llWhisper(0,"BWind now blowing from Southeast");
}
if (msg=="w") {
windRose ="West ";
windDir=(180*DEG_TO_RAD);;
llWhisper(0,"BWind now blowing from West");
}
// ----------------------------- END WIND DIRECTION PRESETS ---
///////////////////////////////////////////////////////////////
// BWind Engine - BWIND SPEED PRESETS ////////////////////////
//////////////////////////////////////////////////////////////
//this is where you have to mod to set your desired BWIND Wind PRESETS
// REMEBER THAT THE BOAT SPEED IS SET IN THE TRIM/HUD ROUTINE ABOVE - here you set the max speed only for each wind preset...
//You may add other wind speeds at will, while I think you have plenty...
//WWC racing module preset will be added here upon release... (currently under development)
//8 KNOTS PRESET
if (msg=="8") {
//primary parameters - self explanatory
windSpeed=4.12; //PRECALCULATED WIND SPEED
maxSpeed=4.0; //YOUR BOAT MAX SPEED FOR THIS WIND PRESET
heelTweak=0.7; //YOUR BOAT MAX HEEL FOR THIS WIND PRESET
//Preset Boat Physics
//llSetVehicleFloatParam (VEHICLE_HOVER_HEIGHT,seaLevel); //Vehicle Lift; this is useful for multihull boats
//Preset running... return wind speed status... - THIS BUILDS THE HUD STRING....
llWhisper(0,"BWind speed set to 8 Knots");
windType="8 Knots"; // 8 Knots
}
// 11 KNOTS PRESET
if (msg=="11") {
//primary parameters - see above for explanation (8 Knots Preset)
windSpeed=5.70;
maxSpeed=4.5;
heelTweak=0.8;
//Preset Boat Physics
//llSetVehicleFloatParam (VEHICLE_HOVER_HEIGHT,seaLevel); //Vehicle Lift
//Preset running... return wind speed status...
llWhisper(0,"BWind speed set to 11 Knots");
windType="11 Knots"; // 11 Knots
}
// 15 KNOTS PRESET
// VERY IMPORTANT !!!
// REMEMBER ! : FOLLOWING VALUES MUST BE THE SAME YOU SET IN DEFAULT WIND SPEED AT LINE 143
if (msg=="15") {
//primary parameters - see above for explanation (8 Knots Preset)
windSpeed=7.75;
maxSpeed=5.5;
heelTweak=0.85;
//Preset Boat Physics
//llSetVehicleFloatParam (VEHICLE_HOVER_HEIGHT,seaLevel); //Vehicle Lift
//Preset running... return wind speed status...
llWhisper(0,"BWind speed set to 15 Knots");
windType="15 Knots"; // 15 Knots
}
// 18 KNOTS PRESET
if (msg=="18") {
//primary parameters - see above for explanation (8 Knots Preset)
windSpeed=9.30;
maxSpeed=6.5;
heelTweak=0.95;
//Preset Boat Physics
//llSetVehicleFloatParam (VEHICLE_HOVER_HEIGHT,seaLevel); //Vehicle Lift
//Preset running... return wind speed status...
llWhisper(0,"BWind speed set to 18 Knots");
windType="18 Knots"; // 18 Knots
}
// 21 KNOTS PRESET
if (msg=="21") {
//primary parameters - see above for explanation (8 Knots Preset)
windSpeed=9.5;
maxSpeed=7.0;
heelTweak=1.0;
//Preset Boat Physics
//llSetVehicleFloatParam (VEHICLE_HOVER_HEIGHT,seaLevel); //Vehicle Lift
//Preset running... return wind speed status...
llWhisper(0,"BWind speed set to 21 Knots");
windType="21 Knots"; // 21 Knots
}
// 25 KNOTS PRESET
if (msg=="25") {
//primary parameters - see above for explanation (8 Knots Preset)
windSpeed=11.3;
maxSpeed=7.5;
heelTweak=1.1;
//Preset Boat Physics
//llSetVehicleFloatParam (VEHICLE_HOVER_HEIGHT,seaLevel); //Vehicle Lift
//Preset running... return wind speed status...
llWhisper(0,"BWind speed set to 25 Knots");
windType="25 Knots"; // 25 Knots
}
}
// ------------------------------ END WIND SPEED PRESETS ---
//////////////////////////////////////////////////////////////
// UTILITIES ////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//Help Message
//here you may build your own help message, shown when you type 'help' without quotes in chat...
if (msg=="help") {
helpString = " ";
helpString+="SAY IN CHAT...\n";
helpString+="------------------------------------------------------------------------------------------------\n";
helpString+="raise - start sailing\n";
helpString+="lower - lower sails (press arrow keys to move around)\n";
helpString+="moor - stop sailing\n";
helpString+="------------------------------------------------------------------------------------------------\n";
helpString+="n,s,e,w,nw,ne,sw,se - set BWind direction\n";
helpString+="8,11,15,18,21,25 - set BWind speed\n";
helpString+="id nnaa (ex. 12AB) - set boat ID\n";
helpString+="hud - hud switch (standard/advanced)\n";
helpString+="spin - hoist/drop Spinnaker (press PgUp & PgDn to trim)\n";
helpString+="gybe - gybe Spinnaker's pole upon tack\n";
helpString+="------------------------------------------------------------------------------------------------\n";
helpString+="HUD colour shows trim settings :\n";
helpString+="|>><<| red = too loose - |><| yellow = off optimum - \n";
helpString+="|=| green = optimum - |<>| cyan = too tight\n";
helpString+="------------------------------------------------------------------------------------------------\n";
llWhisper(0,helpString);
}
// HUD Switch -switch between the two alternative HUD modes : standard and advanced
else if (msg=="hud" && ADV_HUD) ADV_HUD=FALSE;
else if (msg=="hud" && !ADV_HUD) ADV_HUD=TRUE;
}
}
// --------------------------------------- END UTILITIES ---
///////////////////////////////////////////////////
// GLOBAL BOAT CONTROLS //////////////////////////
// you don't want to modify these settings //////
////////////////////////////////////////////////
//Following section maps keyboard keys for boat control... I would NOT edit this section...
control(key id, integer held, integer change) {
//turning controls - LEFT AND RIGHT KEYS
if ( (change & held & CONTROL_LEFT) || (held & CONTROL_LEFT) || (change & held & CONTROL_ROT_LEFT) || (held & CONTROL_ROT_LEFT) ) {
if (sailing) llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<rotSpeed/2.0,0.0,rotSpeed>);
else llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<-rotSpeed,0.0,rotSpeed/1.5>); // left key hold - end
}
else if ( (change & held & CONTROL_RIGHT) || (held & CONTROL_RIGHT) || (change & held & CONTROL_ROT_RIGHT) || (held & CONTROL_ROT_RIGHT) ) {
if (sailing) llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<-rotSpeed/2.0,0.0,-rotSpeed>);
else llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<rotSpeed,0.0,-rotSpeed/1.5>); // right key hold - end
}
else if ( (change & ~held & CONTROL_LEFT) || (change & ~held & CONTROL_ROT_LEFT) ) {
llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<0.0,0.0,0.0>); // left key touched - end
}
else if ( (change & ~held & CONTROL_RIGHT) || (change & ~held & CONTROL_ROT_RIGHT) ) {
llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<0.0,0.0,0.0>); // right key touched - end
}
//sail/throttle controls - UP AND DOWN KEYS
if ( (held & CONTROL_FWD) && (held & CONTROL_UP) ) {
if (sailing) {
sheetAngle+=7;
if (sheetAngle>90) sheetAngle=90;
}
} // up key hold - end
else if ( (held & CONTROL_FWD) || (change & held & CONTROL_FWD) ) {
if (sailing) {
sheetAngle+=2;
if (sheetAngle>90) sheetAngle=90;
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<5.0,0.0,0.0>);
}
} // up key touched - end
else if ( (held & CONTROL_BACK) && (held & CONTROL_UP) ) {
if (sailing) {
sheetAngle-=7;
if (sheetAngle<5) sheetAngle=5;
} else {
currSpeed-=0.3;
if (currSpeed<-2) currSpeed=-2;
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<currSpeed,0,0>);
}
} // down key hold - end
else if ( (held & CONTROL_BACK) || (change & held & CONTROL_BACK) ) {
if (sailing) {
sheetAngle-=2;
if (sheetAngle<5) sheetAngle=5;
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<-5.0,0.0,0.0>);
} else {
currSpeed-=0.3;
if (currSpeed<-2) currSpeed=-2;
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<currSpeed,0,0>);
}
} // down key touched - end
//PGUP/PGDN Spinnaker control
else if (change & held & CONTROL_UP) {
TrimSpinPlus();
}
else if (change & held & CONTROL_DOWN) {
TrimSpinMinus();
}
}
// ------------------------------------- END GLOBAL BOAT CONTROLS ---
///////////////////////////////////////////////////
// GLOBAL BOAT TIMER /////////////////////////////
// you don't want to modify these settings //////
////////////////////////////////////////////////
//IMPORTANT !!! THIS IS THE ACTUAL BOAT CYCLE INVOKING ALL SAILING ROUTINES
//WHATEVER YOU DELETE HERE WILL AFFECT ACTUAL ENGINE BEHAVIOUR
//HERE YOU MAY ADD YOUR OWN PERSONAL FUNCTIONS IF NEEDED AND YOU KNOW WHAT TO DO...
link_message(integer from,integer to,string msg,key id) {
}
timer() {
calcTrueWindAngle();
calcAppWindAngle(); // invoke wind angle calculation routine
if (SAIL_UP) calcBoomDelta(); // invoke sail trim calculation routine
calcHeelAngle(); // invoke heel calculation routine
calcSpeed(); // invoke speed calculation routine
calcLeeway(); // invoke leeway calculation routine
calcTurnRate(); // invoke turn rate routine
if (HUD_ON) updateHUD(); // update hud on cycle
llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<currSpeed,leeway,0>); // boat linear movement
llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<heelTorque,0.0,0.0>); // boat angular movement
}
// -------------------------------------- END GLOBAL BOAT TIMER ---
}
// ------------------------------------- END PROGRAMME -----------------------------------------------------------------------