Now the flyer will fly in any sub-region in a mega region, however, the current parcel test keeps it in the sub-region you rezzed it in. This is because each sub-region has it's own parcel. You can remove that test to let the flyer roam EVERYWHERE in a mega region.
I published a version of this script in the OSgrid forums a while ago, back before OpenSim had the llSetKeyframedMotion function.
This version uses llSetKeyframedMotion, so it is smaller and uses fewer SIM resources to fly around.
The script implements a simple flying critter, it moves smoothly in a straight line until it dives into the water, gets too high, or gets too close to the ground, edge of a region or the edge of a parcel. Then it turns smoothly to avoid the boundaries.
You could modify this to stay underwater instead of above and it would make a pretty good fish.
Stick the script in a prim, preferably one longest in the X direction so you can see it turning. Click on it to stop/start. While it is stopped you can use the build menu to rotate and re-direct it.
Code: Select all
// Kitely Flyer
// A script that gets pretty smooth motion and
// moves a critter around sort of like "pathfinding" in SL.
// This version is an example of how to do that using the
// llSetKeyframedMotion function, and now the new osGetRegionSize() function!
//
// Put this script in a prim that is long in the X direction, it will fly in the
// direction of it's local X axis. Paint it with a texture that allows you to
// tell which way is the top (the local Y axis).
//
// This simple critter behaves in the following ways:
// It moves in a straight line until:
// dives into the water but flies back out
// avoids getting close to the ground
// stays below a maximum height
// turns away from the SIM edges
// turns away from parcel boundary edges.
float DEFLECT=0.20; //how hard things deflect me away (m/s/s)
float MAXHEIGHT=20.0; //how high I'm allowed above the ground
float LOOKAHEAD=4.0; //how far to look into the future to avoid stuf
integer run=1; //flag indicating if I'm running or not
list params=[ //make the smoke
PSYS_PART_FLAGS,0 ,
PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_DROP ,
PSYS_SRC_BURST_RATE,0.125,
PSYS_SRC_BURST_PART_COUNT ,1,
PSYS_PART_MAX_AGE,30.0,
PSYS_PART_START_SCALE, <.75,.75,0>, //magenta smoke
PSYS_PART_START_COLOR, <1,0,1>
];
// This function, OKfly, checks for all the places that you don't want your prim to move.
// If your prim moves off the sim edges, your script will stop running.
// If your prim tries to move underground, the script keeps running but motion stops.
// I use parcels to create boundaries where the critter has to stay, so I test
// for parcel boundaries here also.
// You could test for other things, like a fish can't leave the water or a bird
// can't enter water, but fish jump and birds dive so I do that elsewhere.
// Nothing prevents this critter from passing through solid objects, even if it is not
// phantom. But if it is not phantom it will stop when colliding with an avatar.
integer OKfly(vector pos) //return true if OK to move to pos
{
//don't go outside the edges of the MEGA region
vector size=osGetRegionSize();
if (pos.x<=0 || pos.x>=size.x || pos.y<=0 || pos.y>=size.y)
return FALSE;;
if (pos.z<llGround(pos-llGetPos())) //don't go below ground
return FALSE;
//don't leave the current parcel
key curpar = llList2Key(llGetParcelDetails(llGetPos(),[PARCEL_DETAILS_ID]),0);
key nxtpar = llList2Key(llGetParcelDetails(pos, [PARCEL_DETAILS_ID]),0);
if (curpar!=nxtpar)
return FALSE;
//insert other rules here, like fish not allowed out of the water
return TRUE; //otherwise, it is OK to fly here!
}
//Single Frame motion routines
// These 3 functions impliment smooth motion using llSetKeyFramedMotion
// The way it works is this:
// You call SFrame with a new position, rotation and time
// I usually call SFrame once a second from the timer event with an updated
//position and rotation.
//But it works over longer times and distances.
//UNLIKE llSetKeyframedMotion, here you specify the region co-ordinates and rotation!
//I did it in these tree functions to make it modular, I can replace all 3 of them
//with routines that work on grids that don't have llSetKeyframedMotion
SFsetup() //any necessary setup
{ //This setup is necessary in SL, and may cause errors on Open Sim, if so delete it
llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);
}
//move the calling root prim from its current position and rotation
//to the requested position and rotation in the requested time.
//UNLIKE llSetKeyframedMotion, you specify the region co-ordinates and rotation!
SFrame(vector pos,rotation rot, float seconds)
{
llSetKeyframedMotion([pos-llGetPos(),rot/llGetRot(),seconds],
[KFM_MODE,KFM_FORWARD,KFM_DATA,KFM_TRANSLATION|KFM_ROTATION]);
}
SFstop() //stop the key framed motion
{
llSetKeyframedMotion([],[KFM_COMMAND,KFM_CMD_STOP]);
}
SFnotat() //call this from your not_at_target event
{ //not necessary with llSetKeyframedMotion, so this function is a NOP
}
default
{
state_entry()
{
llOwnerSay("reset");
llParticleSystem(params); //Start the smoke
llSetTimerEvent(1.0); //recalculte movement every second
SFsetup();
}
on_rez(integer param)
{ //turn to a random direction on rez
llSetRot(llEuler2Rot(<0,llFrand(PI-PI/2.0),llFrand(TWO_PI)>));
}
timer() //every second, calculate a new direction to turn and move
{
vector pos=llGetPos(); //get my current position
rotation rot=llGetRot(); //and rotation
vector vel=<1,0,0>*rot; //use my direction as velocity
//here are the RULES that give this critter behavior:
//first (four) rule(s): Avoid the edges of the sim.
vector xvel=llVecNorm(<vel.x,0,0>); //get orthagonal components of velocity
vector yvel=llVecNorm(<0,vel.y,0>);
if (!OKfly(pos+LOOKAHEAD*xvel)) //so you can pong off the edges of the sim
vel -= DEFLECT*xvel; //slow down as you approach X edge.
if (!OKfly(pos-LOOKAHEAD*xvel)) //checking both sides makes me
vel += DEFLECT*xvel; //accelerate away from walls
if (!OKfly(pos+LOOKAHEAD*yvel)) //do the same thing in Y
vel -= DEFLECT*yvel;
if (!OKfly(pos-LOOKAHEAD*yvel))
vel += DEFLECT*yvel;
//I could use LOOKAHEAD to avoid running into the water (I do that for the
//ground below) but I thought it would be fun to allow this flyer to hit the
//water and THEN accelerate it up to fly back out.
float wat=llWater(ZERO_VECTOR);
if (pos.z<wat) //after I have already dipped into the water,
vel += <0,0,1>*DEFLECT; //accelerate back up
//if you don't have some sort of MAXHEIGHT test, the critter would fly up
//and never come back. I turn back at MAXHEIGHT above the land OR water.
//(if you just tested land, you would have trouble when the water was
//over MAXHEIGHT deep)
float gnd=llGround(ZERO_VECTOR);
if (gnd>wat)
wat=gnd; //use the max of water and ground for height limit
if (pos.z>(wat+MAXHEIGHT)) //if I get too high
vel -= <0,0,1>*DEFLECT; //accelerate back down
//When the critter gets within LOOKAHEAD meters of the ground, I start
//accelerating back up. Using the ground normal makes it turn sideways
//away from cliffs instead of always turning straight up.
vector npos=pos+vel; //next position
if ((npos.z-LOOKAHEAD)<gnd) //if my next position is too close to the ground
vel += llGroundNormal(vel)*DEFLECT; //deflect away from the ground normal
//I'm limiting this critter to 1 meter per second, you could go faster
//but beware, llAxes2Rot requires unit vectors! You would have to
//calculate a separate vector that is the normalized velocity and use that below.
vel = llVecNorm(vel); //limit my velocity to 1m/sec
//here I convert the velocity vector into a rotation. These steps result in the
//prim always rotating to keep the head "up". Actually the local Y axis is always
//parallell to the XY plane, the local Z axis is allowed to rotate away from
//straight up to turn the nose to rise or fall.
vector lft=llVecNorm(<0,0,1>%vel);
rot = llAxes2Rot(vel,lft,vel%lft); //calculate new rotation in direction of vel
//SFrame will refuse to go somplace that OKfly does not like, but I do
//another test here and try to do something smarter to avoid getting stuck.
if (!OKfly(pos+vel)) //final test: If I'm still going out of bounds,
{
if (llVecMag(lft)<0.5) //detect Gymbol lock!
lft=<0,1,0>; //and make a hard turn in this unusual case.
vel = llVecNorm(vel+lft*(llFrand(2.0)-1.0)); //randomly turn left or right
lft=llVecNorm(<0,0,1>%vel); //to try to get out of edge lock
rot = llAxes2Rot(vel,lft,vel%lft); //re-calc the rotation
vel=ZERO_VECTOR; //stop and wait for rotation to turn me
}
SFrame(pos+vel,rot,1.0); //start moving and turning
}
//once SFrame is running, even the build dialog cannot change the position
//or rotation without it being changed back. So I added this touch to stop the
//critter so I could move it or rotate it, then touch start it moving again.
touch_start(integer num)
{
if ((run = run^1) ==0) //toggle the run flag
{
llSetTimerEvent(0);
SFstop(); //demonstrate how to use SFstop!
llParticleSystem([]); //turn off the smoke while stopped.
llOwnerSay("stopped");
}
else
{
llSetTimerEvent(1.0); //the next call to SFrame in timer will start it up again
llParticleSystem(params);
llOwnerSay("running");
}
}
}