Copy and paste in the "Control" script //Flying Tako Controls Script //by Kokakola Design //Avil 2007 //version settings integer raceVersion=TRUE; integer practiceVersion=FALSE; integer motorVersion=FALSE; //integer practiceVersion=TRUE; integer raceVersion=FALSE; integer motorVersion=FALSE; //integer motorVersion=TRUE; integer practiceVersion=FALSE; integer raceVersion=FALSE; string boatName="Kokakola "; string versionNumber=""; //miscellaneous key owner;//boat owner key avatar;//person sitting at the helm key kanker="7abbf31e-d601-4521-ae52-f7457d6e0012"; integer ownerVersion=TRUE; integer SAIL_UP=FALSE; integer permSet=FALSE; integer HUD_ON=TRUE; string idStr; integer racing=FALSE; integer numTouches=0; integer sailing=TRUE; integer motoring=FALSE; list dataList; integer phantom=FALSE; //script module flags integer CONTROLS_MODULE=1; integer SAIL_MODULE=2; integer MOTOR_MODULE=3; integer COLORIZER_MODULE=4; //reused math variables vector eulerRot; vector currEuler; rotation quatRot; rotation currRot; vector fwdVec; vector upVec; vector leftVec; //linear motion variables float currSpeed; vector groundSpeed=ZERO_VECTOR; float spdFactor=0.0; float leeway; float rotSpeed=0.7; float rotDelta; vector eulerTurnLeft; vector eulerTurnRight; rotation quatTurnLeft=ZERO_ROTATION; rotation quatTurnRight=ZERO_ROTATION; float rotTweak=0.8; //scales boat turning rate (80% for now) float maxSpeedMotor=10.0; //heeling variables float heelAngle; float heelTorque; float heelAdd; //environment float seaLevel; float windDir; //we can set this in the practice version float windSpeed; //we can set this in the practice version //linked parts integer BOW; integer HULL; integer STERN; integer SAIL; integer WINDVANE; integer PORTBENCH; integer STARBOARDBENCH; integer RUDDER; integer TILLER; integer CENTERBOARD; integer FLOOR; integer MAST; integer TRANSOM; integer REARFLOOR; integer HUD; integer NUMBER1; integer NUMBER2; integer PROP; //wind algorithm parameters float rate=1; float avgAng=45; float avgSpd=3; float halfArc=0.2; float spdRng=1; /////////////////////////////////////////////////////////////////////////////////// //not for public release: integer codeKey=0; integer pingChannel=0; integer secretChannel; integer todaysChannel() { return 0; } /////////////////////////////////////////////////////////////////////////////////// //set initial vehicle parameters setVehicleParams() { llSetVehicleType (VEHICLE_TYPE_BOAT); llSetVehicleRotationParam(VEHICLE_REFERENCE_FRAME,ZERO_ROTATION); 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); //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,0.8); llSetVehicleFloatParam (VEHICLE_BANKING_TIMESCALE,1); //vertical control llSetVehicleFloatParam (VEHICLE_HOVER_HEIGHT,seaLevel); llSetVehicleFloatParam (VEHICLE_HOVER_EFFICIENCY,2.0); llSetVehicleFloatParam (VEHICLE_HOVER_TIMESCALE,1.0); llSetVehicleFloatParam (VEHICLE_BUOYANCY,1.0); } setVehicleParamsMotor() { //vertical attractor llSetVehicleFloatParam (VEHICLE_VERTICAL_ATTRACTION_TIMESCALE,1.0); llSetVehicleFloatParam (VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY,0.8); //banking llSetVehicleFloatParam (VEHICLE_BANKING_EFFICIENCY,1.0); llSetVehicleFloatParam (VEHICLE_BANKING_MIX,1.0); llSetVehicleFloatParam (VEHICLE_BANKING_TIMESCALE,1); } //automatically detect link nums for each named part getLinkNums() { integer i; integer linkcount=llGetNumberOfPrims(); for (i=1;i<=linkcount;++i) { string str=llGetLinkName(i); if (str=="bow") BOW=i; if (str=="hull") HULL=i; if (str=="stern") STERN=i; if (str=="sail") SAIL=i; if (str=="mast") MAST=i; if (str=="windindicator") WINDVANE=i; if (str=="floor") FLOOR=i; if (str=="transom") TRANSOM=i; if (str=="tiller") TILLER=i; if (str=="rudder") RUDDER=i; if (str=="centerboard") CENTERBOARD=i; if (str=="starboardbench") STARBOARDBENCH=i; if (str=="portbench") PORTBENCH=i; if (str=="rearfloor") REARFLOOR=i; if (str=="hud") HUD=i; if (str=="number1") NUMBER1=i; if (str=="number2") NUMBER2=i; if (str=="prop") PROP=i; } } //set camera position for third person view setCamera() { llSetCameraEyeOffset(<-11,0,11>); llSetCameraAtOffset(<3,0,3.5>); } //figure out where to put boat when it is rezzed setInitialPosition() { vector pos=llGetPos(); float groundHeight=llGround(ZERO_VECTOR); float waterHeight = llWater(ZERO_VECTOR); seaLevel=llWater(ZERO_VECTOR); upright(); if (llGetRegionName()=="Gray" && owner==kanker) //if (FALSE) { rotation initRot=llEuler2Rot(<0,0,225*DEG_TO_RAD>); //llSetRot(ZERO_ROTATION); vector initPos; if (raceVersion) initPos=<36.934,142.546,20.1>;//starting position at my dock if (practiceVersion) initPos=<40.046,139.434,20.1>;//starting position at my dock - practice version if (motorVersion) initPos=<33.728,145.8,20.1>; //initPos=<21,150,20.1>;//test position out in the water if (llVecDist(llGetPos(),initPos)<2.0) { llSetRot(initRot); while (llVecDist(llGetPos(),initPos)>.001) llSetPos(initPos); } else { //if over water, set boat height to sealevel + 0.12m if (groundHeight<=waterHeight) { pos.z=waterHeight+0.12; while (llVecDist(llGetPos(),pos)>.001) llSetPos(pos); } } } else { //if over water, set boat height to sealevel + 0.12m; if (groundHeight <= waterHeight) { pos.z = waterHeight + 0.12; while (llVecDist(llGetPos(),pos)>.001) llSetPos(pos); } } } //set sit target for helmsperson setSitTarget() { llSetSitText("SkipperOn"); rotation avRot=llEuler2Rot(<0,0,PI_BY_TWO>); llSitTarget(<-9.1, 0, 2.2>, <0.0, 0.0, 1.0, 90.0>); llSetText("",ZERO_VECTOR,1.0); } //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); } //not sure if i can use this yet moor() { llMessageLinked(LINK_THIS,SAIL_MODULE,"moor",NULL_KEY); llMessageLinked(LINK_THIS,MOTOR_MODULE,"off",NULL_KEY); llOwnerSay("Mooring."); if (permSet) llStopAnimation("helm"); upright(); llReleaseControls(); llSetStatus(STATUS_PHYSICS,FALSE); llSetTimerEvent(0); currSpeed=0; //llResetScript(); } //reset stuff 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(HUD,1000,"",NULL_KEY); //reset HUD llMessageLinked(SAIL,1000,"",NULL_KEY); //reset sail llMessageLinked(WINDVANE,1000,"",NULL_KEY); //turn off windindicator setCamera(); currSpeed=0; if (practiceVersion) { windDir=45*DEG_TO_RAD; windSpeed=3; llSetLinkColor(MAST,<0.3,0.3,0.3>,ALL_SIDES); string tmp=boatName+versionNumber+" Touring"; llSetObjectName(tmp); if (owner==kanker) llMessageLinked(LINK_THIS,COLORIZER_MODULE,"color lemon",NULL_KEY); } if (raceVersion) { idStr=llGetObjectDesc(); string tmp=boatName+versionNumber+" Race"; if (idStr!="(No Description)" & idStr!="") { tmp+=" #"+idStr; llSetObjectName(tmp); } else llSetObjectName(tmp); llSetLinkColor(MAST,<0.9,0.9,0.9>,ALL_SIDES); if (owner==kanker) llMessageLinked(LINK_THIS,COLORIZER_MODULE,"color <0.2,0.6,0.7>",NULL_KEY); } if (motorVersion) { string tmp="Koka Motor "+versionNumber; llSetObjectName(tmp); llSetLinkColor(MAST,<1.0,1.0,1.0>,ALL_SIDES); if (owner==kanker) llMessageLinked(LINK_THIS,COLORIZER_MODULE,"color green",NULL_KEY); //llSetLinkColor(MAST,<0.35,0.5,0.6>,ALL_SIDES); } llMessageLinked(LINK_THIS,SAIL_MODULE,"reset",NULL_KEY); llMessageLinked(LINK_THIS,MOTOR_MODULE,"reset",NULL_KEY); llMessageLinked(LINK_THIS,COLORIZER_MODULE,"reset",NULL_KEY); llListen(0,"",owner,""); if (raceVersion) llListen(pingChannel,"",NULL_KEY,""); llOwnerSay("Ready."); //llOwnerSay("free memory: "+(string)llGetFreeMemory()); } ////////////////////////////////////////////////////////////////////////////////////////////// //state default/////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// default { state_entry() { startup(); llSetStatus(STATUS_BLOCK_GRAB,TRUE); llCollisionSound("", 0.0); llPlaySound("mareblu",1.0); llLoopSound("mareblu",1.0); llSetCameraEyeOffset(<-12.0, 0.0, 5.0> ); llSetCameraAtOffset(<10.0, 0.0, 2.0> ); } on_rez(integer param) { //reset(); llResetScript(); } changed(integer change) { avatar=llAvatarOnSitTarget(); if (change & CHANGED_LINK) { if (avatar==NULL_KEY) { if (!(llGetAgentInfo(owner) & AGENT_ON_OBJECT)) { llMessageLinked(WINDVANE,1000,"",NULL_KEY);//turn off windindicator if (SAIL_UP) { llOwnerSay("Mainsail Lowered."); llMessageLinked(LINK_THIS,SAIL_MODULE,"changed",NULL_KEY); } if (permSet) llStopAnimation("helm"); if (permSet) llReleaseControls(); permSet=FALSE; llResetScript(); } } else { if (ownerVersion && avatar!=owner) llWhisper(0,"Only the owner can operate this boat."); else if ((llGetAgentInfo(owner) & AGENT_ON_OBJECT)) { //llMessageLinked(WINDVANE,4,"",NULL_KEY);//turn on windindicator llWhisper(0,"Click-On the Sail for Armed the Sail. Say 'notecard' for instructions or 'raise' to Start Sailing."); llPlaySound("mareblu",1.0); llLoopSound("mareblu",1.0); if (llAvatarOnSitTarget()==owner) llRequestPermissions(owner,PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION); } } } } 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") { llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY); } else if (msg=="raise") { sailing=TRUE; motoring=FALSE; if (!permSet) llRequestPermissions(owner,PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION); permSet=TRUE; llStartAnimation("helm"); llPlaySound("mareblu",1.0); llLoopSound("mareblu",1.0); llSetStatus(STATUS_PHYSICS,TRUE); llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY); llMessageLinked(LINK_THIS,MOTOR_MODULE,"off",NULL_KEY); //if (practiceVersion) llMessageLinked(WINDVANE,2000+llRound(windDir),"",NULL_KEY); //else llMessageLinked(WINDVANE,4,"",NULL_KEY); } else if (msg=="lower") llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY); else if (llGetSubString(msg,0,4)=="color") llMessageLinked(LINK_THIS,COLORIZER_MODULE,msg,NULL_KEY); else if (msg=="moor") { moor(); llResetScript(); } else if (llGetSubString(msg,0,3)=="vane") { if (llGetSubString(msg,5,-1)=="off") llMessageLinked(WINDVANE,1000,"",NULL_KEY); else if (llGetSubString(msg,5,-1)=="on") { if (practiceVersion) llMessageLinked(WINDVANE,2000+llRound(windDir),"",NULL_KEY); else llMessageLinked(WINDVANE,4,"",NULL_KEY); } } else if (llGetSubString(msg,0,3)=="anim") { if (llGetSubString(msg,5,-1)=="off") { if (permSet) llStopAnimation("helm"); } else if (llGetSubString(msg,5,-1)=="on") { if (permSet) llStartAnimation("helm"); } } else if (llGetSubString(msg,0,7)=="wind dir") { if (!raceVersion) { windDir=(float)llGetSubString(msg,8,-1); string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate; llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY); } } else if (llGetSubString(msg,0,7)=="wind spd") { if (!raceVersion) { windSpeed=(float)llGetSubString(msg,8,-1); string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate; llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY); } } else if (llGetSubString(msg,0,4)=="dir+-") { if (!raceVersion) { halfArc=(integer)llGetSubString(msg,5,-1); string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate; llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY); } } else if (llGetSubString(msg,0,4)=="spd+-") { if (!raceVersion) { spdRng=(float)llGetSubString(msg,5,-1); string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate; llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY); } } else if (llGetSubString(msg,0,3)=="rate") { if (!raceVersion) { rate=(float)llGetSubString(msg,4,-1); string str="windparams "+(string)windDir+" "+(string)windSpeed+" "+(string)halfArc+" "+(string)spdRng+" "+(string)rate; llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY); } } else if (llGetSubString(msg,0,1)=="id") { if (raceVersion) { llMessageLinked(LINK_THIS,COLORIZER_MODULE,msg,NULL_KEY); if (llGetSubString(msg,3,-1)!="off") { idStr=llGetSubString(msg,3,-1); string tmp=boatName+" #"+idStr; llSetObjectName(tmp); llSetObjectDesc(idStr); } } } else if (msg=="hud") { if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY); else llMessageLinked(LINK_THIS,MOTOR_MODULE,msg,NULL_KEY); } else if (llGetSubString(msg,0,4)=="alpha") llMessageLinked(LINK_THIS,COLORIZER_MODULE,msg,NULL_KEY); else if (msg=="motor") { if (motorVersion) { if (!permSet) llRequestPermissions(owner,PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION); permSet=TRUE; llStartAnimation("helm"); llSetStatus(STATUS_PHYSICS,TRUE); llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY); llMessageLinked(LINK_THIS,MOTOR_MODULE,msg,NULL_KEY); motoring=TRUE; sailing=FALSE; llMessageLinked(WINDVANE,1000,"",NULL_KEY); //turn off wind indicator setVehicleParamsMotor(); llOwnerSay("Motoring"); } else llOwnerSay("No motor!"); } else if (msg=="off") { if (motorVersion) { sailing=TRUE; motoring=FALSE; setVehicleParams(); if (practiceVersion) llMessageLinked(WINDVANE,2000+llRound(windDir),"",NULL_KEY); else llMessageLinked(WINDVANE,4,"",NULL_KEY); llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY); llMessageLinked(LINK_THIS,MOTOR_MODULE,msg,NULL_KEY); } } else if (msg=="knots") { llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY); llMessageLinked(LINK_THIS,MOTOR_MODULE,msg,NULL_KEY); } if (msg=="register") { if (raceVersion) { integer chan=todaysChannel(); llShout(pingChannel,"ping"); llSleep(2.0); string regPacket=idStr+","+(string)llGetOwner(); llShout(chan,regPacket); } else { llSay(0,"Your Boat is not race legal and cannot be registered."); } } else if (msg=="pp") { if (!raceVersion) { if (!phantom) { llOwnerSay("phantom ON"); llSetStatus(STATUS_PHANTOM,TRUE); phantom=TRUE; } else { llOwnerSay("phantom OFF"); llSetStatus(STATUS_PHANTOM,FALSE); phantom=FALSE; } } } else if (msg=="practice") { if (!raceVersion) llMessageLinked(LINK_THIS,SAIL_MODULE,msg,NULL_KEY); } } } if (msg=="notecard") { llGiveInventory(id,"Sailing the Tako"); llGiveInventory(id,"Instructions"); } } else if (channel==pingChannel) { secretChannel=todaysChannel(); llListen(secretChannel,"",NULL_KEY,""); } else if (channel==secretChannel) { string str="windparams "+msg; llMessageLinked(LINK_THIS,SAIL_MODULE,str,NULL_KEY); } } 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; } } control(key id, integer held, integer change) { //turning controls if ( (change & held & CONTROL_LEFT) || (held & CONTROL_LEFT) || (change & held & CONTROL_ROT_LEFT) || (held & CONTROL_ROT_LEFT) ) { if (sailing) llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,); else llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<-rotSpeed,0.0,rotSpeed/1.5>); } 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,); } else if ( (change & ~held & CONTROL_LEFT) || (change & ~held & CONTROL_ROT_LEFT) ) { llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<0.0,0.0,0.0>); } else if ( (change & ~held & CONTROL_RIGHT) || (change & ~held & CONTROL_ROT_RIGHT) ) { llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,<0.0,0.0,0.0>); } //sail/throttle controls if ( (held & CONTROL_FWD) && (held & CONTROL_UP) ) { if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,"fwd_big",NULL_KEY); else { currSpeed+=0.3; if (currSpeed>maxSpeedMotor) currSpeed=maxSpeedMotor; llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,); llMessageLinked(LINK_THIS,MOTOR_MODULE,"fwd",NULL_KEY); } } else if ( (held & CONTROL_FWD) || (change & held & CONTROL_FWD) ) { if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,"fwd_small",NULL_KEY); else { currSpeed+=0.3; if (currSpeed>maxSpeedMotor) currSpeed=maxSpeedMotor; llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,); llMessageLinked(LINK_THIS,MOTOR_MODULE,"fwd",NULL_KEY); } } else if ( (held & CONTROL_BACK) && (held & CONTROL_UP) ) { if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,"back_big",NULL_KEY); else { currSpeed-=0.3; if (currSpeed<-2) currSpeed=-2; llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,); llMessageLinked(LINK_THIS,MOTOR_MODULE,"back",NULL_KEY); } } else if ( (held & CONTROL_BACK) || (change & held & CONTROL_BACK) ) { if (sailing) llMessageLinked(LINK_THIS,SAIL_MODULE,"back_small",NULL_KEY); else { currSpeed-=0.3; if (currSpeed<-2) currSpeed=-2; llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,); llMessageLinked(LINK_THIS,MOTOR_MODULE,"back",NULL_KEY); } } //motor to idle if ( (change & held & CONTROL_DOWN ) ) { if (motoring) { currSpeed=0.0; llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,<0,0,0>); llMessageLinked(LINK_THIS,MOTOR_MODULE,"idle",NULL_KEY); } } } link_message(integer from,integer to,string str,key id) { if (to==CONTROLS_MODULE) { dataList=llCSV2List(str); currSpeed=llList2Float(dataList,0); leeway=llList2Float(dataList,1); rotSpeed=rotTweak*llList2Float(dataList,2); heelTorque=5*llList2Float(dataList,3); llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION,); llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,); } } } Copy and paste in the "Sail" script //Flying Tako Sail Script //by Kanker Greenacre //July 2005 //version settings integer raceVersion=TRUE; integer practiceVersion=FALSE; integer motorVersion=FALSE; integer olympicVersion=FALSE; //integer practiceVersion=TRUE; integer raceVersion=FALSE; integer motorVersion=FALSE; integer olympicVersion=FALSE; //integer olympicVersion=TRUE; integer motorVersion=FALSE; integer practiceVersion=FALSE; integer raceVersion=FALSE; string boatName="Kokakola "; string versionNumber=""; //script module flags integer CONTROLS_MODULE=1; integer SAIL_MODULE=2; integer MOTOR_MODULE=3; integer COLORIZER_MODULE=4; //environment vector wind; float windAngle; float absWindAngle; float depth; 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 boomAngle; integer currBoomAngle=0; integer delta; integer incr; float optBoomAngle; float trimFactor; //performance constants float timerFreq=1.5; //timer frequency, seconds float maxSpeed=10.0; //maximum allowed speed integer sheetAngle=5; //initial sheet angle setting integer ironsAngle=35; //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=0.5; //scales leeway (0=no leeway) float speedTweak=1.0; //scales speed float rotTweak=5.0; //scales boat turning rate float heelTweak=0.5; //scales amount of heeling float maxWindSpeed=14.0; //used for heeling calculation float windDir; //we can set this in the practice version float windSpeed; //we can set this in the practice version //motor settings integer fromMotoring=FALSE; float maxSpeedMotor=10.0; float throttle=0; //miscellaneous string dataString; key owner;//boat owner key avatar;//person sitting at the helm key kanker="7abbf31e-d601-4521-ae52-f7457d6e0012"; integer ownerVersion=TRUE; integer SAIL_UP=FALSE; integer permSet=FALSE; integer HUD_ON=TRUE; string idStr; integer racing=FALSE; integer numTouches=0; integer sailing=TRUE; integer motoring=FALSE; float mpsToKts=1.944; float convert=1; string units=" m/s"; integer showKnots=FALSE; float time; float offset; float theta; integer practice=FALSE; //linked parts integer BOW; integer HULL; integer STERN; integer SAIL; integer WINDVANE; integer PORTBENCH; integer STARBOARDBENCH; integer RUDDER; integer TILLER; integer CENTERBOARD; integer FLOOR; integer MAST; integer TRANSOM; integer REARFLOOR; integer HUD; integer NUMBER1; integer NUMBER2; integer PROP; //wind algorithm parameters float angRate=10; float avgAng=0; float avgSpd=5; float halfArc=0.2; float spdRng=3; integer raceWindOn=FALSE; //prevailing wind algorithm calcWindDir() { time=llGetTimeOfDay(); theta=time/14400*TWO_PI*angRate; offset=llSin(theta)*llSin(theta*2)*llSin(theta*9)*llCos(theta*4); windDir=avgAng+halfArc*offset; llMessageLinked(WINDVANE,2000+llRound(windDir*RAD_TO_DEG),"",NULL_KEY); } calcWindSpd() { offset=llSin(theta)*llSin(theta*4)+llSin(theta*13)/3; windSpeed=avgSpd+spdRng*offset; if (windSpeed<0) windSpeed=0; } //calculate wind relative to boat calcWindAngle() { if (raceWindOn) { calcWindDir(); calcWindSpd(); } else { wind=llWind(ZERO_VECTOR); windDir=llAtan2(-wind.y,-wind.x);//direction wind is blowing FROM windSpeed=llVecMag(wind); } 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 } //calculate heel angle based on wind and sail settings calcHeelAngle() { heelAngle=llAsin(leftVec.z); if (SAIL_UP) if (llFabs(windAngle+boomAngle)>3*DEG_TO_RAD) heelTorque=SAIL_UP*llSin(windAngle)*llCos(heelAngle)*PI_BY_TWO*(windSpeed/maxWindSpeed)*llCos(boomAngle*DEG_TO_RAD)*heelTweak; else heelTorque=0; else heelTorque=0; heelAdd=heelTorque-heelAngle; eulerRot=; quatRot=llEuler2Rot(eulerRot); } //calculate angle of sail (or boom) based on sheet setting and the wind calcBoomDelta() { boomAngle=sheetAngle; if (boomAngle>llFabs(windAngle*RAD_TO_DEG)) boomAngle=llRound(llFabs(windAngle*RAD_TO_DEG)); if (windAngle<0) boomAngle*=-1; delta=boomAngle-currBoomAngle; currBoomAngle=boomAngle; llMessageLinked(SAIL,delta,"",NULL_KEY);//tell sail to rotate by delta } //calculate boat speed calcSpeed() { groundSpeed=llGetVel(); absWindAngle=llFabs(windAngle); if (llFabs(absWindAngle*RAD_TO_DEG-llFabs(boomAngle))<10) trimFactor=0; else { optBoomAngle=0.5*absWindAngle*RAD_TO_DEG; trimFactor=(90.-llFabs(optBoomAngle-llFabs(boomAngle)))/90.; } if (absWindAnglemaxSpeed) currSpeed=maxSpeed; } else currSpeed*=0.8; } } //calculate leeway (lateral drift) due to wind calcLeeway() { leeway=SAIL_UP*-llSin(windAngle)*llSin(heelAngle)*windSpeed*leewayTweak; } //calculate turning rate based on current speed calcTurnRate() { spdFactor=llVecMag(groundSpeed)/(maxSpeed); //rotSpeed=0.4-(spdFactor)*0.4; rotSpeed=0.5+(spdFactor)/2.0; } //gets depth of water below boat calcDepth() { depth=llWater(ZERO_VECTOR)-llGround(ZERO_VECTOR); } //update heads-up display (in 1st and 3rd person view) updateHUD() { compass=PI_BY_TWO-zRotAngle; while (compass<0) compass+=TWO_PI; calcDepth(); dataString ="Heading: " +(string)((integer)(compass*RAD_TO_DEG))+" deg\n"; dataString+="Wind Angle: " +(string)((integer)(windAngle*RAD_TO_DEG))+" deg\n"; dataString+="Wind Speed: " +llGetSubString((string)(windSpeed*convert),0,3)+units+"\n"; dataString+="Ground Speed: "+llGetSubString((string)(llVecMag(groundSpeed*convert)),0,3)+units+"\n"; dataString+="Depth: " +llGetSubString((string)depth,0,3)+" m\n"; dataString+="Sail Angle: " +(string)(-boomAngle)+" deg\n"; dataString+="Sheet Angle: " +(string)sheetAngle+" deg\n\n\n\n"; llSetText(dataString,ZERO_VECTOR,1.0); llMessageLinked(HUD,1001,dataString,NULL_KEY);//sends data to HUD prim above and behind the boat } //automatically detect link nums for each named part getLinkNums() { integer i; integer linkcount=llGetNumberOfPrims(); for (i=1;i<=linkcount;++i) { string str=llGetLinkName(i); if (str=="bow") BOW=i; if (str=="hull") HULL=i; if (str=="stern") STERN=i; if (str=="sail") SAIL=i; if (str=="mast") MAST=i; if (str=="windindicator") WINDVANE=i; if (str=="floor") FLOOR=i; if (str=="transom") TRANSOM=i; if (str=="tiller") TILLER=i; if (str=="rudder") RUDDER=i; if (str=="centerboard") CENTERBOARD=i; if (str=="starboardbench") STARBOARDBENCH=i; if (str=="portbench") PORTBENCH=i; if (str=="rearfloor") REARFLOOR=i; if (str=="hud") HUD=i; if (str=="number1") NUMBER1=i; if (str=="number2") NUMBER2=i; if (str=="prop") PROP=i; } } //raise sail: start timer raiseSail() { SAIL_UP=TRUE; llOwnerSay("Raising MainSail."); llMessageLinked(SAIL,1002,"",NULL_KEY); llMessageLinked(WINDVANE,4,"",NULL_KEY); llSetTimerEvent(timerFreq); } //lower sail but leave physics on lowerSail() { llOwnerSay("Lowering MainSail."); llMessageLinked(SAIL,1000,"",NULL_KEY);//lower sail w/ reset currBoomAngle=0; sheetAngle=5; SAIL_UP=FALSE; } ////////////////////////////////////////////////////////////////////////////////////////////// //state default/////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// default { state_entry() { getLinkNums(); llSetText("",ZERO_VECTOR,1.0); llMessageLinked(HUD,1001,"",NULL_KEY); avgAng=300*DEG_TO_RAD; halfArc=18*DEG_TO_RAD; } on_rez(integer param) { raceWindOn=FALSE; } link_message(integer from,integer to,string msg,key id) { if (from==LINK_ROOT && to==SAIL_MODULE) { if (msg=="fwd_small") { sheetAngle+=2; if (sheetAngle>90) sheetAngle=90; } else if (msg=="back_small") { sheetAngle-=2; if (sheetAngle<5) sheetAngle=5; } else if (msg=="fwd_big") { sheetAngle+=7; if (sheetAngle>90) sheetAngle=90; } else if (msg=="back_big") { sheetAngle-=7; if (sheetAngle<5) sheetAngle=5; } else if (llGetSubString(msg,0,4)=="sheet") { incr=(integer)llDeleteSubString(msg,0,4); sheetAngle+=incr; if (sheetAngle>90) sheetAngle=90; } else if (msg=="raise") raiseSail(); else if (msg=="lower") lowerSail(); else if (msg=="moor") { llSetTimerEvent(0); if (SAIL_UP) lowerSail(); } else if (llGetSubString(msg,0,9)=="windparams") { if (!practiceVersion | practice) { list in=llParseString2List(msg,[" "],[""]); avgAng=llList2Integer(in,1)*DEG_TO_RAD; avgSpd=llList2Integer(in,2); if (llGetListLength(in)==6) { halfArc=llList2Integer(in,3)*DEG_TO_RAD; spdRng=llList2Float(in,4); angRate=llList2Integer(in,5)*10.0; } raceWindOn=TRUE; } } else if (msg=="hud") { if (HUD_ON) { HUD_ON=FALSE; llSetText("",ZERO_VECTOR,1.0); llMessageLinked(HUD,1001,"",NULL_KEY); } else HUD_ON=TRUE; } else if (msg=="motor") { if (SAIL_UP) lowerSail(); llSetTimerEvent(0); motoring=TRUE; sailing=FALSE; } else if (msg=="off") { llSetTimerEvent(timerFreq); motoring=FALSE; sailing=TRUE; } else if (msg=="reset") llResetScript(); else if (msg=="knots") { if (showKnots==FALSE) { convert=mpsToKts; units=" kts"; showKnots==TRUE; } else { convert=1; units=" m/s"; showKnots=FALSE; } } else if (msg=="practice") { if (practice) { llOwnerSay("practice wind OFF"); practice=FALSE; raceWindOn=FALSE; } else { llOwnerSay("practice wind ON"); practice=TRUE; raceWindOn=TRUE; } } } } timer() { calcWindAngle(); if (SAIL_UP) calcBoomDelta(); calcHeelAngle(); calcSpeed(); calcLeeway(); calcTurnRate(); if (HUD_ON) updateHUD(); dataString=(string)currSpeed+","+(string)leeway+","+(string)rotSpeed+","+(string)heelTorque; llMessageLinked(LINK_THIS,CONTROLS_MODULE,dataString,NULL_KEY); } }