The online racing simulator
Question, was playing with the new JRR packet.

Right now I'm spawning a concrete slab below where I spawn them so that I am able to spawn them in the air(since ground checking is enforced), but I am currently trying to implement it so that a car can "hover" above another car(the slab is no big deal, as it prevents the car from falling on top of the other car which when that happens they intersect and go flying).

But my problem is. I can't seem to get the heading to match the heading of the car it's hovering above. Is there some weird math that is being done? from what LFS outputs with MCI, and what JRR takes in?

I tried the existing code we used in another PRISM plugin that takes the data and uses it to place a chalk arrow on the ground... and that still seems to be off...

here's the code from the plugin for layout objects, should the same code work? if so, something might be bugged either in PRISM's handling of the data or the way LFS handles it(or I'm misunderstanding how it should work).

<?php 
#Code from CompCar/MCI
$x $Info->X/65536;
$y $Info->Y/65536;
$obj = new LayoutObject($x$y, ($Info->Z/65536) + 3LayoutObject::$OBJ_REAL_OBJECT180 $Info->Heading/32768 180LayoutObject::$AXO_CONCRETE_SLAB, ($Info->PLID 4));

$AXM = new IS_AXM();
$AXM->PMOAction(PMO_ADD_OBJECTS)->Info(array($obj))->send();
#JRR X,Y,Z code is slightly different($x & $y divided by 4096 instead of 65536)
#I'd include it but it's on my other computer...
#but no matter what I try for the heading, I can't get it to match up.
?>

..
Quote from cargame.nl :As far as I can understand JRR uses the lyt heading;

https://www.lfs.net/programmer/lyt

NOTE2 :
-------
Heading represents 360 degrees in 256 values.

Heading = (heading_in_degrees + 180) * 256 / 360

128 : heading of zero
192 : heading of 90 degrees
0 : heading of 180 degrees
64 : heading of -90 degrees

While MCI has its own;

# direction of forward axis : 0 = world y direction, 32768 = 180 deg


So some sort of conversion formula needs to be used.

Hmm, ok, when I get home I will look closer. it may be a matter of I'm rotating too much(May only need to use 90 instead of 180. don't know. will find out in about an hour
..
Quote from cargame.nl :Well rougly I came to this;


$JRR_heading = round($MCI['direction'] / 182);


Probably needs to be more precise but JRR cannot be very precise, because 'only' 256 steps. So this is a problem if you want to use JRR for very precise operations.

256 may be precise enough. we will find out.I mean it's not a super big deal that they're EXACTLY the same, but roughly the same would be nice. I think I tried dividing by 182, but did not round it... so that may be why it didn't work. will try it when I get home.
I think to convert from MCI heading to ObjectInfo heading you do this:

- Shift it right 8 bits (i.e. divide by 256) so it has 256 values (0 to 255).
- Add 128 (i.e. rotate by 180 degrees) to align it with the ObjectInfo headings.
Quote from Scawen :I think to convert from MCI heading to ObjectInfo heading you do this:

- Shift it right 8 bits (i.e. divide by 256) so it has 256 values (0 to 255).
- Add 128 (i.e. rotate by 180 degrees) to align it with the ObjectInfo headings.

Yep. that worked great. Sadly, what I was playing with can never be more than a proof of concept, but never the less, it's kinda cool I'm really glad to finally have this packet added ... and as much as other might not like to hear this. It really has great potential on cruise servers.

For anyone interested here is the PRISM plugin I ended up with:
Spoiler - click to reveal

<?php 
php
class example extends Plugins
{
    
// Set our URL, Name, Author, Version, Description
    
const URL '';
    const 
NAME 'JRRTesting';
    const 
AUTHOR 'T3charmy';
                    
#Year.Month.Day.Time in 24 hour (UTC)
    
const VERSION '16.2.18.0132';
    const 
DESCRIPTION 'Proof of Concept JRR spawning';
    
    protected 
$location = array();
    protected 
$tows = array();
    protected 
$slabs = array();

    public function 
__construct() {
        
$this->registerPacket('OnPlayerPit'ISP_PLP);
        
$this->registerPacket('onPlayerLeave'ISP_PLL);
        
$this->registerPacket('onMCI'ISP_MCI);
        
        
$this->registerSayCommand('tow''cmdTow''');
    }
    
    public function 
OnPlayerPit(IS_PLP $PLP)
    {
        unset(
$this->tows[$PLP->PLID]);
    }

    public function 
OnPlayerLeave(IS_PLL $PLL)
    {
        unset(
$this->tows[$PLL->PLID]);
    }
    
    public function 
onMCI(IS_MCI $MCI)
    {
        foreach (
$MCI->Info as $CompCar) {
            
$towby array_search($CompCar->PLID$this->tows); # to check for who is towing said user
            
            
$X = ($CompCar->4096);
            
$Y = ($CompCar->4096);
            
$Z = ($CompCar->65536);
            
$Heading $CompCar->Heading;
            
console($Heading);
            
$this->location[$CompCar->PLID] = array('X' => $X'Y' => $Y'Z' => $Z'Heading' => $Heading);
            
console("{$CompCar->PLID} : towed by: $towby");
            if(
$towby !== false){
                
$towLoc $this->location[$towby];
                
$newCarLoc = new ObjectInfo();
                
$newCarLoc->X($towLoc['X']);
                
$newCarLoc->Y($towLoc['Y']);
                
$newCarLoc->Z($towLoc['Z'] + 4);
                
$newCarLoc->Flags(128);
                
$newCarLoc->Index(0);
                
$newHeading = ($towLoc['Heading'] / 256) + 128;
                if(
$newHeading >= 256) {
                    
$newHeading -= 256;
                } elseif (
$newHeading 0) {
                    
$newHeading += 256;
                }
                
$newCarLoc->Heading($newHeading);
                
$deleted false;
                if(isset(
$this->slabs[$towby])){
                    if(
$this->slabs[$towby]->x() !== ($towLoc['X'] / 16)) {
                        
IS_AXM()->PMOAction(PMO_DEL_OBJECTS)->Info(array($this->slabs[$towby]))->Send();
                        
$deleted true;
                        
console('deleted');
                    }
                }
                
$this->slabs[$towby] = new LayoutObject(($towLoc['X'] / 16), ($towLoc['Y'] / 16), $towLoc['Z'] + 2LayoutObject::$OBJ_REAL_OBJECT, (90 $towLoc['Heading']/32768 180), LayoutObject::$AXO_CONCRETE_SLAB, ($CompCar->PLID 4));
                if(
$deleted){
                    
IS_AXM()->PMOAction(PMO_ADD_OBJECTS)->Info(array($this->slabs[$towby]))->Send();
                }
                
IS_JRR()->PLID($CompCar->PLID)->UCID(0)->JRRAction(JRR_RESET_NO_REPAIR)->StartPos($newCarLoc)->Send();
                
//Need to add a concrete slab above "towing" user using $location 1 and remove slab at location 2
            
}
        }
    }
    
    public function 
cmdTow($cmd$ucid)  
    {
        
$strcsv str_getcsv($cmd' ');
        list(
$cmd$towing) = $strcsv;

        
$CurrentClient $this-getPlayerByUCID($ucid);
        
$TargetClient $this-getPlayerByUName($towing);
        
$this->tows[$CurrentClient->PLID] = $TargetClient->PLID;
        
console("create tow {$CurrentClient->PLID} :: {$TargetClient->PLID}");
    }
    

}
?>

I couldn't get it working for these reasons (see comments):

<?php 
    
public function cmdTow($cmd$ucid)  
    {
        
$strcsv str_getcsv($cmd' ');
        list(
$cmd$towing) = $strcsv;

        
// Returns array so can't access PLID directly
        
$CurrentClient $this-getPlayerByUCID($ucid);

        
// UName doesn't seem to be defined in PlayerHandler class, so getPlayerByUName can't find it
        
$TargetClient $this-getPlayerByUName($towing);

        
$this->tows[$CurrentClient->PLID] = $TargetClient->PLID;
        
console("create tow {$CurrentClient->PLID} :: {$TargetClient->PLID}");
    }
?>

So I added this to the plugin just to try to make it work:

<?php 
        
// Get just the first member of array
        
foreach ($CurrentClient as $player) {
            
$CurrentPlayer $player;
            break;
        }

        
$this->tows[$CurrentPlayer->PLID] = $TargetClient->PLID;
        
console("create tow {$CurrentPlayer->PLID} :: {$TargetClient->PLID}");
?>

And added $UCID property to PlayerHandler class and added this to the constructor:

<?php 
$this
->UName $this->parent->clients[$NPL->UCID]->UName;
?>

Now it recognizes the username, but still not towing.

Edit: It tows when I add the $PLID property to PlayerHandler Shrug
With a slight modification in onMCI, the towed car now follows the towing car behind it. It looks choppy but interesting Big grin

Spoiler - click to reveal

<?php 
    
public function onMCI(IS_MCI $MCI)
    {
        foreach (
$MCI->Info as $CompCar) {
            
$towby array_search($CompCar->PLID$this->tows); # to check for who is towing said user

            
$X = ($CompCar->4096);
            
$Y = ($CompCar->4096);
            
$Z = ($CompCar->65536);
            
$Heading $CompCar->Heading;
            
console($Heading);
            
$this->location[$CompCar->PLID] = array('X' => $X'Y' => $Y'Z' => $Z'Heading' => $Heading);
            
console("{$CompCar->PLID} : towed by: $towby");
            if(
$towby !== false){
                
$towLoc $this->location[$towby];
                
$newCarLoc = new ObjectInfo();
                
$towHeading deg2rad(round($Heading 182) - 90); // behind the towing car
                
$newCarLoc->X($towLoc['X'] + 100 cos($towHeading));
                
$newCarLoc->Y($towLoc['Y'] + 100 sin($towHeading));
                
$newCarLoc->Z($towLoc['Z']);
                
$newCarLoc->Flags(128);
                
$newCarLoc->Index(0);
                
$newHeading = ($towLoc['Heading'] / 256) + 128;
                if(
$newHeading >= 256) {
                    
$newHeading -= 256;
                } elseif (
$newHeading 0) {
                    
$newHeading += 256;
                }
                
$newCarLoc->Heading($newHeading);
                
$deleted false;
                if(isset(
$this->slabs[$towby])){
                    if(
$this->slabs[$towby]->x() !== ($towLoc['X'] / 16)) {
//                        IS_AXM()->PMOAction(PMO_DEL_OBJECTS)->Info(array($this->slabs[$towby]))->Send();
                        
$deleted true;
                        
console('deleted');
                    }
                }
                
$this->slabs[$towby] = new LayoutObject(($towLoc['X'] / 16), ($towLoc['Y'] / 16), $towLoc['Z'] + 2LayoutObject::$OBJ_REAL_OBJECT, (90 $towLoc['Heading']/32768 180), LayoutObject::$AXO_CONCRETE_SLAB, ($CompCar->PLID 4));
                if(
$deleted){
//                    IS_AXM()->PMOAction(PMO_ADD_OBJECTS)->Info(array($this->slabs[$towby]))->Send();
                
}
                
IS_JRR()->PLID($CompCar->PLID)->UCID(0)->JRRAction(JRR_RESET_NO_REPAIR)->StartPos($newCarLoc)->Send();
                
//Need to add a concrete slab above "towing" user using $location 1 and remove slab at location 2
            
}
        }
    }
?>

Quote from Flame CZE :With a slight modification in onMCI, the towed car now follows the towing car behind it. It looks choppy but interesting Big grin

Yea, if you want it to be more smooth, you could increase the PPS in PRISM's host config. I believe 25PPS is the max. Also I probably broke it when I was last min rewriting it to clean it up, and get rid of useless code... Hm. I will have to look at PRISM's core. UName should be defined in Player Handler

Also Nice! I was gonna make it follow behind, but my math skills were a little rough, so I just threw it together this way. I think I would like following behind better honestly... but increasing PPS may cause the car to spawn inside you... not sure.

Edit: Fixed this(getPlayerbyUName) in 0.5.0.6 of PRISM which I will release later today.
Quote from T3charmy :Also Nice! I was gonna make it follow behind, but my math skills were a little rough, so I just threw it together this way. I think I would like following behind better honestly... but increasing PPS may cause the car to spawn inside you... not sure.

It's still not perfect, for instance when you turn while towing, the towed car keeps the same angle and distance, not how you'd expect a towed car to be following... if you know what I mean. I'll see if I can code it so it works as if the car was a trailer, so it'd "bend" as you turn. Interesting challenge Big grin

Quote from T3charmy :Edit: Fixed this(getPlayerbyUName) in 0.5.0.6 of PRISM which I will release later today.

Nice Thumbs up
Quote from Flame CZE :It's still not perfect, for instance when you turn while towing, the towed car keeps the same angle and distance, not how you'd expect a towed car to be following... if you know what I mean. I'll see if I can code it so it works as if the car was a trailer, so it'd "bend" as you turn. Interesting challenge Big grin

I'm interested to see what you come up with. Smile
Did anyone any progress with such tow? I'm back to LFS for a while and maybe will try to do such thing. You don't have to increase PPS. You just can have towing car data stored and send more often JRR packets.

The other thing - LFS will die, becuase of this crap S3 and no more big events, so don't spend too much time on this Smile
When a car gets teleported by insim to new position, the car's speed is reset to zero.
Even if the update-rate of the teleport-script is very high that makes it appear 'laggy' and unsmooth.
I think "real" car-towing is not possible like that, but maybe some sort of rescue-car to get cars out of gravel traps:
Rescue-car arrives at stuck car, positions itself.
Rescue-car driver activates command.
Stuck car gets teleported behind rescue-car.
..

FGED GREDG RDFGDR GSFDG