
As always, this set of scripts is designed to allow turning a big build composed of multiple objects (=multiple linksets) into a vehicle.
Alright, changes in this version:
- rotation of the main object now controls all the other objects' rotation. This means I can add fancy things like mouselook control in the near future
- simultaneous use of remote and control should be possible now
- movement has been smoothed out for finer control, the vehicle now accelerates and decelerates in finer steps
Control script (this one goes in the seat child prim of your main object, you will need to adapt the sit target and camera offset parameters in this script)
CODE
// MultiMove Controller Script
//
// Manages pilot seat and movement for the whole set
integer unichan = -79997; // this is the channel used by the controller and
// objects to sync and move
float deltat = 0.1; // interval between updates
integer controlstaken; // control keys taken by the script, initialised later
integer listener;
integer maxl = 4;
integer pressed;
integer change;
vector sitoffset = <0.2,0,-0.5>; // sitting position
vector camoffset = <-24,0,9>; // camera position
vector camtarget = <12,0,0>; // camera direction
float speed = 2.0; // distance to move within deltat
float vspeed = 1.0; // vertical distance to move within deltat
float rspeed = 0.3183; // angle in degrees to rotate left or right within deltat
float inertia = 0.8;
float moment = 0.5;
vector velocity;
float rotacity;
vector accel = <0.125, 0.125, 0.06125>;
float raccel = 0.06125;
rotation rtarget;
default
{
on_rez(integer c)
{
llResetScript();
}
state_entry()
{
llMinEventDelay(deltat);
llSitTarget(sitoffset, ZERO_ROTATION);
llSetCameraAtOffset(camtarget);
llSetCameraEyeOffset(camoffset);
controlstaken = CONTROL_FWD|CONTROL_BACK|CONTROL_ROT_LEFT|CONTROL_ROT_RIGHT|CONTROL_LEFT|CONTROL_RIGHT|CONTROL_UP|CONTROL_DOWN;
}
changed(integer c)
{
if (c & CHANGED_LINK)
{
key id = llAvatarOnSitTarget();
if (id != NULL_KEY)
{
if (id == llGetOwner())
{
llRequestPermissions(id, PERMISSION_TAKE_CONTROLS);
} else llUnSit(id);
} else {
llReleaseControls();
llSetTimerEvent(0.0);
}
}
}
run_time_permissions(integer p)
{
if (p & PERMISSION_TAKE_CONTROLS)
{
integer n;
rtarget = llGetRootRotation();
velocity = ZERO_VECTOR;
rotacity = 0.0;
for (n=0; n<maxl; ++n)
llShout(unichan + n, (string)(llGetRootPosition() + llGetRegionCorner()) + "*" + (string)rtarget);
llSleep(1.0);
llTakeControls(controlstaken, TRUE, FALSE);
llSetTimerEvent(deltat);
} else {
llReleaseControls();
}
}
control(key id, integer level, integer edge)
{
pressed = level;
//change = edge;
}
timer()
{
vector target = llGetRootPosition() + llGetRegionCorner();
rotation rtarget = llGetRootRotation();
if (pressed)
{
if (pressed & CONTROL_ROT_RIGHT)
{
// turning left
rotacity -= raccel;
if (rotacity < -rspeed) rotacity = -rspeed;
} else if (pressed & CONTROL_ROT_LEFT)
{
// turning right
rotacity += raccel;
if (rotacity > rspeed) rotacity = rspeed;
} else rotacity *= moment;
if (pressed & CONTROL_FWD)
{
// going forward
velocity.x += accel.x;
if (velocity.x > speed) velocity.x = speed;
} else if (pressed & CONTROL_BACK)
{
// going backward
velocity.x -= accel.x;
if (velocity.x < -speed) velocity.x = -speed;
} else velocity.x *= inertia;
if (pressed & CONTROL_UP)
{
// going up
velocity.z += accel.z;
if (velocity.z > vspeed) velocity.z = vspeed;
} else if (pressed & CONTROL_DOWN)
{
// going down
velocity.z -= accel.z;
if (velocity.z < -vspeed) velocity.z = -vspeed;
} else velocity.z *= inertia;
if (pressed & CONTROL_LEFT)
{
// going up
velocity.y += accel.y;
if (velocity.y > speed) velocity.y = speed;
} else if (pressed & CONTROL_RIGHT)
{
// going down
velocity.y -= accel.y;
if (velocity.y < -speed) velocity.y = -speed;
} else velocity.y *= inertia;
} else {
velocity *= inertia;
rotacity *= moment;
}
rtarget *= llEuler2Rot(<0,0,rotacity>);
target += velocity * rtarget;
if ((velocity.x == 0.0) && (velocity.y == 0.0) && (velocity.z == 0.0) && (rotacity == 0.0))
return;
listener = (listener + 1) % maxl;
llShout(unichan + listener, (string)target + "*" + (string)rtarget);
}
}
Remote script (put this one in any child prim of your main build, IM me for the RemoteHUD object or indications on how to make one)
CODE
// Multimove Autopilot
//
// For navigating big space cruisers
integer unichan = -79997;
integer listener;
integer maxl = 4;
float rate = 0.1;
integer my_chan = 128;
integer handle;
string opt;
list buttons;
integer vel;
integer vvel;
integer lvel;
integer rvel;
float speed = 2.0;
float vspeed = 1.0;
float lspeed = 1.0;
float rspeed = 0.3183;
float inertia = 0.8;
float moment = 0.5;
vector velocity;
float rotacity;
vector accel = <0.125, 0.125, 0.06125>;
float raccel = 0.06125;
rotation rtarget;
default
{
on_rez(integer p)
{
llSleep(2.0);
llResetScript();
}
state_entry()
{
llSetTimerEvent(0.0);
velocity = ZERO_VECTOR;
rotacity = 0.0;
rtarget = llGetRootRotation();
vel = 0;
vvel = 0;
lvel = 0;
rvel = 0;
opt = "";
buttons = [];
for (listener = 0; listener < maxl; listener = listener + 1)
llShout(unichan + listener, (string)(llGetRootPosition() + llGetRegionCorner()) + "*" + (string)rtarget);
handle = llListen(my_chan, "", "", "");
}
listen(integer chan, string name, key id, string msg)
{
if ((llGetOwner() != id) && (llGetOwnerKey(id) != llGetOwner())) return;
string t = llGetSubString(msg, 0, 0);
if (t == "m") { vel = (integer)llDeleteSubString(msg, 0, 0); llSetTimerEvent(rate); } else
if (t == "v") { vvel = (integer)llDeleteSubString(msg, 0, 0); llSetTimerEvent(rate); } else
if (t == "l") { lvel = (integer)llDeleteSubString(msg, 0, 0); llSetTimerEvent(rate); } else
if (t == "r") { rvel = (integer)llDeleteSubString(msg, 0, 0); llSetTimerEvent(rate); } else
if (t == "k") { llSetTimerEvent(0.0); llShout(unichan, "k"); }
}
timer()
{
vector pos = llGetRootPosition() + llGetRegionCorner();
listener = (listener + 1) % maxl;
if (rvel != 0)
{
rotacity += rvel * raccel;
if (rotacity > rspeed) rotacity = rspeed;
if (rotacity < -rspeed) rotacity = -rspeed;
} else rotacity *= moment;
if (vel != 0)
{
velocity.x += vel * accel.x;
if (velocity.x > speed) velocity.x = speed;
if (velocity.x < -speed) velocity.x = -speed;
} else velocity.x *= inertia;
if (vvel != 0)
{
velocity.z += vvel * accel.z;
if (velocity.z > vspeed) velocity.z = vspeed;
if (velocity.z < -vspeed) velocity.z = -vspeed;
} else velocity.z *= inertia;
if (lvel != 0)
{
velocity.y += lvel * accel.y;
if (velocity.y > lspeed) velocity.y = lspeed;
if (velocity.y < -lspeed) velocity.y = -lspeed;
} else velocity.y *= inertia;
if (rotacity != 0.0)
{
rtarget = llGetRootRotation() * llEuler2Rot(<0,0,rotacity>);
} else {
if ((velocity.x == 0.0) && (velocity.y == 0.0) && (velocity.z == 0.0)) llSetTimerEvent(0.0);
}
llShout(unichan + listener, (string)(pos + velocity * rtarget) + "*" + (string)rtarget);
}
}
Move (copy this script four times, called move 0, move 1, move 2, move 3; put all four copies in EACH linkset's root prim)
CODE
// Move
//
// Multimove follower
integer unichan = -79997; // this is the channel used by the controller and
// objects to sync
integer handle; // handle for the listen function
vector my_offset; // original offset, set at first activation
rotation my_orientation; // original rotation, set at first activation
integer my_num; // position in the chain of redundancy
float azimut;
default
{
on_rez(integer p)
{
llResetScript();
}
state_entry()
{
my_num = (integer)llGetSubString(llGetScriptName(), -1, -1);
handle = llListen(unichan + my_num, "", "", "");
}
listen(integer chan, string name, key id, string mesg)
{
if (mesg == "k") llDie();
if (mesg == "s") return;
integer index = llSubStringIndex(mesg, "*");
my_offset = llGetPos() + llGetRegionCorner() - (vector)llGetSubString(mesg, 0, index - 1);
my_orientation = llGetRot() / (rotation)(llDeleteSubString(mesg, 0, index));
my_offset = my_offset / llGetRot();
state running;
}
state_exit()
{
llListenRemove(handle);
}
}
state running
{
on_rez(integer p)
{
state default;
}
state_entry()
{
handle = llListen(unichan + my_num, "", "", "");
}
listen(integer chan, string name, key id, string mesg)
{
if (mesg == "k")
{
llDie();
} else if (mesg == "s")
{
state default;
} else {
list info = llParseString2List(mesg, ["*"], []);
rotation rtarget = my_orientation * (rotation)llList2String(info, 1);
vector target = my_offset * rtarget + (vector)llList2String(info, 0) - llGetRegionCorner();
llSetPrimitiveParams([PRIM_POSITION, target, PRIM_ROTATION, rtarget]);
}
}
state_exit()
{
llListenRemove(handle);
}
}
