Jump to content

Motorcycle script revisited


Mollymews
 Share

You are about to reply to a thread that has been inactive for 207 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

as there was a August 2020 update to vehicle region crossings due to the Cloud Uplift programme I had a play with the Cory/Andrew Linden example motorcycle script

what I found (as have others) is that because the new network protocol method of transferring vehicle and agent is now a lot faster, the outcome is that the race condition between vehicle and agent has been accentuated, meaning that the race to script condition fail happens a lot sooner when it does fail. Sooner than it did before, breaking quite a few more vehicle scripts today (because fast race) than broke in the past (because slow race). Agent Not found

i read that Simon Linden is going to have a look at this. Try to fix the race condition. I have some thoughts on the server side fix for this but that is for another day/post. I just focus here on what we can do ourselves in LSL now today

the animation agent not found error, happens in two main places.  In the control event and in the timer event. Events when not scripted/handled well also cause the half-unsit problem (which is itself caused by the bounding box animation problem - avatar when animated sits higher than it should)

then there is the 4 corner crossing problem. We can't auto-fix this from LSL, but we can in some cases recover by providing a manual teleport method which in the recovery case will reconcile the agent with the vehicle, and allow the agent to resume control of the vehicle without having to stand and resit 

in this mod of the faithful Cory/Andrew Linden motorcycle script, I have put in all the techniques (from way back in the day when I was into making vehicles for myself) that worked in the slow old days and which still work today in the fast new world

 

// example motorcycle script
//
// Originally written by Cory Linden.
// Then modified and tweaked by Andrew Linden for the forum script library.

// Then modified by Mollymews (August 2020) to add animation changeouts and 
// to ameliorate vehicle/agent race condition on region crossings

// Root prim should be oriented such that its local X-, Y- and Z-axes are
// parallel to forward, left, and up respectively.
//
// Sound triggers are commented out but not removed, so if you
// want sounds, just add the sounds to the cycle's contents and uncomment
// the triggers.
//
// Be careful when changing parameters.  Some of them can be very
// sensitive to change, such that a change of less than 5% can have a
// noticable effect.  You can tell some (but not necessarily all) of the
// more sensitive settings in this example by looking for the ones that
// have been set to double precission.  Changing only one at a time is a
// good idea.
//
// The geometry of the motorcycle itself can have significant impact on
// whether it in a straight line when not trying to turn.  For best results
// use asymmetric design with as wide of a base as you can tolerate.

// These are globals only for convenience (so when you need to modify
// them you need only make a single change).  There are other magic numbers
// below that have not yet been pulled into globals.


// Mollymews: half the Linden speeds. Downtuned to putter along
float gMaxTurnSpeed = 6;      // 12;
float gMaxWheelieSpeed = 2.5; // 5;
float gMaxFwdSpeed = 15;      // 30;
float gMaxBackSpeed = -5;     // -10;
float gAngularRamp = 0.17;
float gLinearRamp = 0.2;

// These are true globals whose values are "accumulated" over
// multiple control() callbacks.
float gBank = 0.0;
vector gLinearMotor = <0, 0, 0>;
vector gAngularMotor = <0, 0, 0>;

// Mollymews: animation. Change to animations of your own 
/*
string ANIM_IDLE = "Ninja Idle";
string ANIM_FWD = "Ninja Ride";
string ANIM_BACK = "Ninja Ride";
string ANIM_LEFT = "Ninja left";
string ANIM_RIGHT = "Ninja right";
string ANIM_BRAKE = "Ninja Ride";
string ANIM_WHEELIE = "Ninja Ride";
*/

string ANIM_IDLE = "motorcycle_sit";
string ANIM_FWD = "motorcycle_sit";
string ANIM_BACK = "motorcycle_sit";
string ANIM_LEFT = "motorcycle_sit";
string ANIM_RIGHT = "motorcycle_sit";
string ANIM_BRAKE = "motorcycle_sit";
string ANIM_WHEELIE = "motorcycle_sit";

// Mollymews: safeties
string gAnim;
key gRiderKey;
integer gRiderLink;
integer gTimerType;
integer gTimerTicks;


default
{
  state_entry()
  {
    // init stuff that never changes
    llSetSitText("Ride");
    //llCollisionSound("", 0.0);
    
    // llSitTarget(<0.12, -0.02, 0.7>, ZERO_ROTATION); // Mollymews: for Molly bike
    llSitTarget(<0.2, 0.05, 0.6>, ZERO_ROTATION); // Linden motorcycle_sit bike 

    // Mollymews: move Linden camera settings to permissions event
    //  llSetCameraEyeOffset(<-6.0, 0.0, 1.0>);
    //  llSetCameraAtOffset(<3.0, 0.0, 1.0>);  
    // create the vehicle
    llSetVehicleFlags(-1);
    llSetVehicleType(VEHICLE_TYPE_CAR);
    llSetVehicleFlags(VEHICLE_FLAG_LIMIT_MOTOR_UP
      | VEHICLE_FLAG_LIMIT_ROLL_ONLY);
    llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY, 0.2);
    llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_EFFICIENCY, 0.8);
    llSetVehicleFloatParam(VEHICLE_ANGULAR_DEFLECTION_TIMESCALE, 0.8);
    llSetVehicleFloatParam(VEHICLE_LINEAR_DEFLECTION_TIMESCALE, 0.3);

    llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_TIMESCALE, 0.8);
    llSetVehicleFloatParam(VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE, 0.4);
    llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_TIMESCALE, 0.01);
    llSetVehicleFloatParam(VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE, 0.35);

    llSetVehicleVectorParam(VEHICLE_LINEAR_FRICTION_TIMESCALE, <1000, 100, 1000>);
    llSetVehicleVectorParam(VEHICLE_ANGULAR_FRICTION_TIMESCALE, <100, 10, 100>);

    llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY, 0.49);
    llSetVehicleFloatParam(VEHICLE_VERTICAL_ATTRACTION_TIMESCALE, 0.44);

    llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY,   3.0);
    llSetVehicleFloatParam(VEHICLE_BANKING_MIX, 0.7);
    llSetVehicleFloatParam(VEHICLE_BANKING_TIMESCALE, 0.01);

    //llSetVehicleFloatParam(VEHICLE_HOVER_HEIGHT, 2.0);
    //llSetVehicleFloatParam(VEHICLE_HOVER_TIMESCALE, 1.0);
    //llSetVehicleFloatParam(VEHICLE_HOVER_EFFICIENCY, 0.5);
  }
  
  changed(integer change)
  {
    if (change & CHANGED_LINK)
    {
      llSetTimerEvent(0.0); // Mollymews: stop any running timer

      key agent = llAvatarOnSitTarget();
      if (agent)
      {
        if (agent != llGetOwner())
        {
          // not the owner ==> boot off  
          llSay(0, "You aren't the owner");
          llUnSit(agent);
          llPushObject(agent, <0,0,100>, ZERO_VECTOR, FALSE);
        }
        else
        {
          // owner has mounted
          // reset the global accumulators
          gAngularMotor = <0, 0, 0>;
          gLinearMotor = <0, 0, 0>;
          gBank = 0.0;
          // Mollymews: remove any residual motion from last ride/rez
          llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY, gBank);
          llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION, gAngularMotor);
          llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, gLinearMotor);
          
          llRequestPermissions(agent, PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);
        }
      }
      else
      {
        // dismount
        llSetStatus(STATUS_PHYSICS, FALSE);      
        llReleaseControls();
        gRiderKey == NULL_KEY; // Mollymews: assign
        //llPlaySound("off", 0.4);
      }
    }
    
    else if (change & CHANGED_REGION)
    {
      // Mollymews: region crossing safeties
      // stop any running timer
      llSetTimerEvent(0.0);

      // slow down, reduce linear speed to 1/4
      gLinearMotor.x *= 0.25;
      llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, gLinearMotor);

      // poll for agent for upto 10 seconds
      integer present;  
      float time = llGetTime();
      // present is TRUE only when agent is actually linked to the bike
      while ((llGetTime() - time < 10.0) &&  !(present = (llGetLinkKey(gRiderLink) == gRiderKey)));            
      if (!present)
      {
        // stop the bike cold so it doesn't run away
        llSetStatus(STATUS_PHYSICS, FALSE);          
        // IM agent with slurl so they can manually teleport
        // Teleporting will put agent back on bike when bike has not gone off-world/region
        // We can't script teleport agent as they are on a different region        
        vector pos = llGetPos();
        string slurl = "secondlife://" + llEscapeURL(llGetRegionName()) 
          + "/"+ (string)((integer)pos.x) 
          + "/"+ (string)((integer)pos.y) 
          + "/"+ (string)(llCeil(pos.z));  
        llInstantMessage(gRiderKey, "We are broken at " + slurl + "\n Click the teleport link to get back on the bike");
        // start waiting for teleport
        gTimerType = 1;
        gTimerTicks = 0;
        llSetTimerEvent(1.0);  
      }
    }
  }

  run_time_permissions(integer perm)
  {
    // Mollymews: animation, controls, camera, rider link number
    if (perm & PERMISSION_TRIGGER_ANIMATION)
    {  
      gAnim = ANIM_IDLE;
      if (gAnim != "") 
      {
        llStartAnimation(gAnim);
        llStopAnimation("Sit");
      }
      if (perm & PERMISSION_TAKE_CONTROLS)
      {   
        llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_RIGHT | CONTROL_LEFT
          | CONTROL_ROT_RIGHT | CONTROL_ROT_LEFT | CONTROL_UP | CONTROL_DOWN, TRUE, FALSE);
        if (perm & PERMISSION_CONTROL_CAMERA)
        {
          llClearCameraParams(); // reset camera to default
          llSetCameraParams([
            CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
            CAMERA_BEHINDNESS_ANGLE, 0.0, // (0 to 180) degrees
            CAMERA_BEHINDNESS_LAG, 0.0, // (0 to 3) seconds
            CAMERA_DISTANCE, 3.0, // ( 0.5 to 10) meters
            CAMERA_FOCUS_LAG, 0.00, // (0 to 3) seconds
            CAMERA_FOCUS_OFFSET, <1.0, 0.0, 0.0>, // <-10,-10,-10> to <10,10,10> meters
            CAMERA_PITCH, 15.0, // (-45 to 80) degrees
            CAMERA_POSITION_LAG, 0.0 // (0 to 3) seconds
          ]);           
          gRiderKey = llGetPermissionsKey();         
          gRiderLink = llGetNumberOfPrims();
          llSetStatus(STATUS_PHYSICS, TRUE);
          // llPlaySound("start", 0.4);
          // llSleep(3.0);
          // llLoopSound("idle", 0.4);
        } 
      }      
    }
  }

  control(key id, integer level, integer edge)
  {
    // The idea here is to ramp up the motors when the keys are held down for a long
    // time and to let the motors decay after they are let go.  This allows fine-
    // tuning of the motion of the vehicle by throttling the key controls.
    //
    // Note that this probably doesn't work well when the client FPS and/or the server
    // FPS is lagging.  So for best results you'll want to turn off as much visual
    // effects as you can tolerate, and drive in the more empty areas.
    
    string anim; // Mollymews: animation

    // linear
    integer key_control = FALSE;
    if(level & CONTROL_FWD)
    {
      anim = ANIM_FWD; // Mollymews: animation  
      gLinearMotor.x = gLinearMotor.x + gLinearRamp * (gMaxFwdSpeed - gLinearMotor.x);
      key_control = TRUE;
    }
    if(level & CONTROL_BACK)
    {
      anim = ANIM_BACK; // Mollymews: animation
      gLinearMotor.x = gLinearMotor.x + gLinearRamp * (gMaxBackSpeed - gLinearMotor.x);
      key_control = TRUE;
    }
    // Mollymews: add brake
    if (level & CONTROL_DOWN)
    {
      anim = ANIM_BRAKE;
      gLinearMotor.x = 0;
      key_control = TRUE;
    }
    if (key_control)
    {
      llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, gLinearMotor);
      key_control = FALSE;
    }
    else
    {
      if (gLinearMotor.x > 10 || gLinearMotor.x < -5)  // Mollymews: slow down: .x 10 was Linden 15
      {
        // Automatically reduce the motor if keys are let up when moving fast.
        gLinearMotor.x *= 0.8;
        llSetVehicleVectorParam(VEHICLE_LINEAR_MOTOR_DIRECTION, gLinearMotor);
      }
      else
      {
        // reduce the linear motor accumulator for the next control() event
        gLinearMotor.x *= 0.8;
      }
    }

    // angular
    if(level & (CONTROL_RIGHT|CONTROL_ROT_RIGHT))
    {
      anim = ANIM_RIGHT; // Mollymews: animation
      gAngularMotor.x = gAngularMotor.x + gAngularRamp * (gMaxTurnSpeed - gAngularMotor.x);
      key_control = TRUE;
    }
    if(level & (CONTROL_LEFT | CONTROL_ROT_LEFT))
    {
      anim = ANIM_LEFT; // Mollymews: animation
      gAngularMotor.x = gAngularMotor.x - gAngularRamp * (gMaxTurnSpeed + gAngularMotor.x);
      key_control = TRUE;
    }
    if(level & CONTROL_UP)
    {
      anim = ANIM_WHEELIE; // Mollymews: animation  
      gAngularMotor.y = gAngularMotor.y - gAngularRamp * (gMaxWheelieSpeed + gAngularMotor.y);
      key_control = TRUE;
    }
    if (key_control)
    {
      // turn on banking and apply angular motor
      gBank = 3.0;
      llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY, gBank);
      llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,gAngularMotor);
      gAngularMotor *= 0.95;   // light attenuation
    }
    else
    {
      if (gAngularMotor.x > 4
          || gAngularMotor.x < -4)
      {
        // We were turning hard, but no longer  ==> reduce banking to help
        // the motorcycle travel straight when bouncing on rough terrain.
        // Also, turn off the angular motor ==> faster upright recovery.
        gAngularMotor *= 0.4;
        gBank *= 0.5;
        llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY, gBank);
        llSetVehicleVectorParam(VEHICLE_ANGULAR_MOTOR_DIRECTION,gAngularMotor);
      }
      else
      {
        // reduce banking for straighter travel when not actively turning
        gAngularMotor *= 0.5;
        gBank *= 0.8;
        llSetVehicleFloatParam(VEHICLE_BANKING_EFFICIENCY, gBank);
      }
    }
     
    // Mollymews: animation. 3 separate ifs for performance efficiency
    if (anim != "")
    {
      if (anim != gAnim)
      {  
        if (llGetLinkKey(gRiderLink) == gRiderKey) // ensure agent is on bike
        {
          llStartAnimation(anim);
          if (gAnim != "") llStopAnimation(gAnim);
          gAnim = anim;       
          gTimerType = 0;  // 0 = animation timer
          llSetTimerEvent(1.0);  // re/start timer for idle animation
        }
      }
    }
    // llLoopSound("run", 0.4);
  }
   
  timer()
  {
    // Mollymews: animation
    // Timer event started on the previous region will fire (at time) when script instantiates 
    //   on the next region
    // This can sometimes happen before changed(CHANGED_REGION) event fires disabling the timer
    //   so we check that agent is actually sittng (linked to the bike) before we attempt to animate
    if (gTimerType == 0) // animation timer
    {
      if (llVecMag(llGetVel()) < 0.1) // bike has pretty much stopped
      {
        llSetTimerEvent(0.0);
        if (llGetLinkKey(gRiderLink) == gRiderKey) // ensure agent is on bike 
        {
          if (gAnim != ANIM_IDLE)
          {
            string anim = gAnim;
            gAnim = ANIM_IDLE;
            if (gAnim != "") llStartAnimation(gAnim);
            if (anim != "") llStopAnimation(anim);
            // llLoopSound("idle", 0.4);
          }    
        }
      }
    }
    else if (gTimerType == 1) // manual teleport recovery
    { 
      if (llGetLinkKey(gRiderLink) == gRiderKey)  
      {
        // agent has manualy teleported and is now on the bike
        // so try to reconcile by requesting permissions again
        llSetTimerEvent(0.0);
        if (gAnim != "") 
        {
          llStopAnimation(gAnim);
          gAnim = "";
        }        
        llRequestPermissions(gRiderKey, PERMISSION_TRIGGER_ANIMATION | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);
      }
      else
      {
        if (++gTimerTicks == 60) // 1 minute
        {
          // we are dead. Standing up manually can often put us next to the bike
          llSetTimerEvent(0.0);          
          // we could tidy up and delete the bike here
          // llDie();
        }
      } 
    }
  }
}

// END //

 

 

 

  • Like 2
  • Thanks 2
Link to comment
Share on other sites

  • 4 weeks later...

been playing with this quite a bit lately and noticed that on some rare occasions on region change that the follow camera can still break, and controls are released/disabled. Has only happened to me 2 times for broken camera, and 1 time for broken controls out of quite a few hundreds region crossings. But still is better if we can fix than not fix as best we can when it happens

so some mods to this script. I just show the edits and you can modify your own script as wanted


add this as a global var

integer gRequestType;

in changed event: (change & CHANGED_LINK) precede permissions request with request type: link change

gRequestType = 0; // 0 = link change request
llRequestPermissions(agent, PERMISSION_TRIGGER_ANIMATION
   | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);

in changed event: (change & CHANGED_REGION) precede permissions request with request type: region change
. Recoding to handle both present and not present polling result

// poll for agent for upto 10 seconds
// note that this drops down to a long poll of 60 seconds when not present in 10 seconds
integer present;  
float time = llGetTime();
// present is TRUE only when agent is actually linked to the bike
while ((llGetTime() - time < 10.0) &&  !(present = (llGetLinkKey(gRiderLink) == gRiderKey)));            
if (present)
{
  // Mollymews: September addition
  // Sometimes the follow camera is broken at this point. We can reset the follow camera here
  // But sometimes also we have no controls either even tho we have permissions, the controls
  //   have been released, and we can't control the bike and it can run away while we are on it
  // We can't test that we have actual controls as the script thinks we have
  //   and we can only get back controls by re-requesting permissions
  // So we re-request permissions on each region change with a flag to distinguish
  //   between region and link (sit) change, to deal with these issues should they happen
  gRequestType = 1; // 1 = region change request
  llRequestPermissions(gRiderKey, PERMISSION_TRIGGER_ANIMATION
    | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);               
}
else // not present. Rider and bike are not on the same region
{
  llSetStatus(STATUS_PHYSICS, FALSE);          
  // IM agent with slurl so they can manually teleport
  // Teleporting will put agent back on bike when bike has not gone off-world/region
  // We can't script teleport agent as they are on a different region        
  vector pos = llGetPos();
  string slurl = "secondlife://" + llEscapeURL(llGetRegionName())
    + "/" + (string)((integer)pos.x)
    + "/" + (string)((integer)pos.y)
    + "/" + (string)(llCeil(pos.z));  
  llInstantMessage(gRiderKey, "We are broken at "
    + slurl + "\n Click the teleport link to get back on the bike");
  // start long poll, waiting for teleport or very slow region change
  gTimerType = 1;
  gTimerTicks = 0;
  llSetTimerEvent(1.0);  
}

in timer event: (gTimerType == 1) precede permissions request with request type: region change

gRequestType = 1; // 1 = region change request
llRequestPermissions(gRiderKey, PERMISSION_TRIGGER_ANIMATION
  | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);

our run_time_permissions event now looks like this:

run_time_permissions(integer perm)
{
  // Mollymews: animation, controls, camera, rider key and link number, start lights and sound
  gRiderKey = NULL_KEY;  // we acquire rider key when have all permissions
  if (perm & PERMISSION_TRIGGER_ANIMATION)
  {  
    if (gRequestType == 0)  // set bike sit effects only on link change
    {
      gAnim = ANIM_IDLE;
      if (gAnim != "")
      {
        llStartAnimation(gAnim);
        llStopAnimation("Sit");
      }
    }
    
    if (perm & PERMISSION_TAKE_CONTROLS)
    {  
      // re/set controls on both region and link change
      llTakeControls(CONTROL_FWD | CONTROL_BACK | CONTROL_RIGHT | CONTROL_LEFT
        | CONTROL_ROT_RIGHT | CONTROL_ROT_LEFT | CONTROL_UP | CONTROL_DOWN, TRUE, FALSE);
      
      if (perm & PERMISSION_CONTROL_CAMERA)
      {
        // re/set follow camera on both region and link change
        llClearCameraParams(); // reset camera to default
        llSetCameraParams([
          CAMERA_ACTIVE, 1, // 1 is active, 0 is inactive
          CAMERA_BEHINDNESS_ANGLE, 0.0, // (0 to 180) degrees
          CAMERA_BEHINDNESS_LAG, 0.0, // (0 to 3) seconds
          CAMERA_DISTANCE, 3.0, // ( 0.5 to 10) meters
          CAMERA_FOCUS_LAG, 0.00, // (0 to 3) seconds
          CAMERA_FOCUS_OFFSET, <1.0, 0.0, 0.0>, // <-10,-10,-10> to <10,10,10> meters
          CAMERA_PITCH, 15.0, // (-45 to 80) degrees
          CAMERA_POSITION_LAG, 0.0 // (0 to 3) seconds
        ]);           
                                    
        if (gRequestType == 0)  // set bike start effects only on link change
        {
          /* Molly bike stuff
          // lights on
          llSetLinkPrimitiveParamsFast(gHeadlight, [PRIM_GLOW, 1, 0.2,
               PRIM_POINT_LIGHT, 1, <1.0, 1.0, 0.75>, 1.0, 10.0, 0.75]);
          llSetLinkPrimitiveParamsFast(gBrakelight, [PRIM_GLOW, 0, 0.2]);
          // sound on
          llPlaySound(SOUND_START, 1.0);
          llSleep(1.0);
          llLoopSound(SOUND_IDLE, 1.0);
          // default direction to FWD
          gDirection = 1;
          */
        }
                    
        // re/acquire rider and physics on both region and link change
        gRiderKey = llGetPermissionsKey();  // acquire rider key       
        gRiderLink = llGetNumberOfPrims();
        llSetStatus(STATUS_PHYSICS, TRUE);
      }
    }       
  }
 
  // test for rider
  if (gRiderKey == NULL_KEY)
    llSay(0, "We don't have all the permissions. Try Stand and Ride again.");
}

 

 

 

Link to comment
Share on other sites

I also add a comment about licensing

Linden stuff is typically Creative Commons 3.0. So if you copied all of this verbatim into your own bike script then you would be bound by the same license

but if you just copy out my edits/additions (polling, permissions handling, effects handling, rider/agent acquiring, teleport, etc) bits into your own bike/vehicle script then you are not bound to Creative Commons 3.0.  My edits are in the Public Domain and you can use them as you want

on the basis that vehicles that perform well/better on region crossings are good for everyone. And if I ever buy your vehicle then that is what I want. A vehicle that doesn't break on region crossings for reasons that are knowable

Edited by Mollymews
Link to comment
Share on other sites

I'd suggest holding off on vehicle script development involving region crossings for a while. There have been recent changes related to cloud uplift, and those changes need fixes. Watch the release notes and come to server user group meetings (Tuesday at noon, Denby) if you're really into this.

Link to comment
Share on other sites

42 minutes ago, animats said:

I'd suggest holding off on vehicle script development involving region crossings for a while. There have been recent changes related to cloud uplift, and those changes need fixes. Watch the release notes and come to server user group meetings (Tuesday at noon, Denby) if you're really into this.

if we are a commercial vehicle builder then I agree with animats. Go to the server group meetings and participate

i am not a commercial builder. I only make stuff for myself to play with.  Maybe one day, Linden will make it so that vehicle region crossings are fully transactional, which I hope will happen. Until then I will try and work my way past issues that adversely affect what I do now today

a thing. I am also documenting here, what can break and where in the script it is broken. It may be helpful to know this

Link to comment
Share on other sites

3 hours ago, Mollymews said:

Maybe one day, Linden will make it so that vehicle region crossings are fully transactional, which I hope will happen. Until then I will try and work my way past issues that adversely affect what I do now today

That's reasonable. I'm just tired of doing workarounds for SL bugs. I'm spending most of my programming time on non-SL projects right now.

Right now, I'm not sure what's working with region crossings and what isn't. Today I did the Drivers of SL drive at well over 100km/hr on one of my own bikes, and nothing broke. Then I did some slow boating north of the Blake Sea area and had four region crossings fail. Twice they failed so badly that I was stuck, then logged out by the system.

Edited by animats
Link to comment
Share on other sites

some more commentary FYI

in the previous edits I shoud have mentioned that, as wrote, in the timer event (gTimerType == 1) we no longer stop gAnim. As in the now edited run_time_permissions event (gRequestType == 1) no animation is played, so we need to continue playing gAnim

the code edit is:

else if (gTimerType == 1) // manual teleport recovery or slow region change
 {
   if (llGetLinkKey(gRiderLink) == gRiderKey)  
   {
     // agent has manualy teleported and is now on the bike
     // so try to reconcile by requesting permissions again
     llRequestType = 1; // 1 = region change   
     llRequestPermissions(gRiderKey, PERMISSION_TRIGGER_ANIMATION |
       PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);
   }
   else
   {
     if (++gTimerTicks == 60) // 1 minute
     {
       // we are dead. Standing up manually can often put us next to the bike
       llSetTimerEvent(0.0);          
       // we could tidy up and delete the bike here
       // llDie();
     }
   }
 }

we could add a 3rd gRequestType for this. 0 = link (sit) change, 1 = region (present) change, 2 = region (not present) change. I leave that to you (the reader) to do if you want

+

other FYI

the effects timer started in control event is actually not necessary. As wrote, all that happens is that it checks the velocity of the bike, and plays the stop effects when the bike effectively stops.  This check and effect can be coded in the control event directly, no timer actually needed for this kind of check and effect

i stuck the timer in the control event so that it would fire timer events to highlight what can happen when on region crossings the timer event does fire and the agent is not present and we don't account for this

+

another FYI  

llGetLinkKey(gRiderLink) == gRiderKey

on a region change the agent is relinked to the vehicle when both are present on the region. We can check this. Until the agent is relinked then llGetNumberOfPrims()will be the same as llGetObjectDetails(OBJECT_PRIM_COUNT). One less than the count including the agent

the caveat to watch for here is that when the agent is relinked on region change, the CHANGED_LINK event is not fired, it is suppressed. Which makes sense, we would not want CHANGED_LINK to fire in this case

we are best to match keys rather than rely on matching number of prims, as the count can be disrupted should SomeRandom Resident sit on the bike before our agent arrives

+

and another FYI

script error: "Camera control currently only supported for attachments and objects on which you are sitting."

this can happen when the agent is prevented by the system from crossing into the destination region. When the script data/state is instantiated on the next region the active follow camera data is applied to the agent. The system, on instantiation, doesn't check before doing so that the agent is actually sitting. It just tells us after the fact that the agent isn't

a way to reproduce this is to go to a General region and change our Maturity rating to General and then try to cross into a Mature region. Chilbo to Kuwol for example

 

Link to comment
Share on other sites

7 hours ago, Mollymews said:

// we are dead. Standing up manually can often put us next to the bike

I once tried using RLV to move a detached avatar back to the bike and re-seat them. But it wasn't reliable, and some people didn't like enabling RLV for that. Moving the bike back to the avatar automatically is an option worth exploring, though. That often works if you're unseated and move the vehicle with edit. It also has a chance of getting the avatar out of that "stuck, can't walk, can't stand, can't fix avatar health" state. Worth playing around in that space.

Link to comment
Share on other sites

On 9/14/2020 at 11:35 AM, Mollymews said:

in the previous edits I shoud have mentioned that, as wrote, in the timer event (gTimerType == 1) we no longer stop gAnim. As in the now edited run_time_permissions event (gRequestType == 1) no animation is played, so we need to continue playing gAnim

 

this is wrong. It re-introduces the half unsit (animation bounding box) problem

in the timer event (gTimerType == 1) we do need to stop the current animation then treat what follows as if the agent has just sat on the bike. (gRequestType = 0). Which as wrote will change the animation to ANIM_IDLE. Changing the animation seems to be a fix for the half unsit problem in this particular situation

should be:

else if (gTimerType == 1) // manual teleport recovery
{ 
  if (llGetLinkKey(gRiderLink) == gRiderKey)  
  {
    // Rider has manually teleported, or has been a very slow region change,
    //   and is now on the bike
    // So try to reconcile by requesting permissions again
    llSetTimerEvent(0.0);
    // stop any playing animation
    if (gAnim!= "") llStopAnimation(gAnim);
    gRequestType = 0; // 0 = reset same as link change
    llRequestPermissions(gRiderKey, PERMISSION_TRIGGER_ANIMATION 
      | PERMISSION_TAKE_CONTROLS | PERMISSION_CONTROL_CAMERA);
  }

 

Link to comment
Share on other sites

  • 3 months later...

adding more FYI to the document

 

in another thread here: https://community.secondlife.com/forums/topic/464397-sailing-body-animation-issues-after-crossings

there was a discussion about CHANGED_LINK event firing on region crossings. I had a moment to think about this more today

 

this can happen when an agent is unable to transfer successfully to the next region where the vehicle is. When so the agent is formally unlinked from the vehicle and the CHANGED_LINK event fires as it should. Sometimes tho it doesn't fire when it should (circumstantially why I dunno exactly for every circumstance)

this behaviour has implications for multi-passenger vehicles. As when a agent is formally unlinked then the count of prims is reduced which can affect the absolute link number of the agent(s)

in multi-passenger vehicles then we can either maintain a link location table (list) or use a look up function to check for the presence of the agent(s). Example look up function:

integer present(key id)
{
   integer c = llGetObjectPrimCount(llGetKey());
   integer i = llGetNumberOfPrims();
   while (i > c)
   {
      if (llGetLinkKey(i) == id)
         return TRUE;
      --i;
   }
   return FALSE;
}

combine this into some wait time loop on CHANGED_REGION

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 207 days.

Please take a moment to consider if this thread is worth bumping.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...