<?php
class ABGap extends Plugins
{
const URL = 'https://www.lfs.net/forum/thread/89031-ABGap-%28code-dump%2C-unsupported%29';
const NAME = 'ABGap';
const AUTHOR = 'notanillusion';
const VERSION = '0.0.7';
const DESCRIPTION = 'Gap delta to car ahead and behind';
const GAP_AHEAD = -1;
const GAP_BEHIND = 1;
const SPLIT_LAP = 255;
public function __construct ()
{
$this->registerPacket('onLap', ISP_LAP); // record times, display gaps
$this->registerPacket('onMci', ISP_MCI); // update positions
$this->registerPacket('onNpl', ISP_NPL); // add new plids
$this->registerPacket('onPll', ISP_PLL); // remove unusued plids
$this->registerPacket('onRst', ISP_RST); // update total splits and laps, fresh db table
$this->registerPacket('onSpx', ISP_SPX); // record times, display gaps
$this->registerPacket('onSta', ISP_STA); // update viewplid
$this->timing = array(); // plid, lap, split, pos, time
$this->playerData = array(); // plid => array(pos, lap), ...
$this->viewPlid = 0;
$this->laps = 0;
$this->splits = 0;
}
/// *** PACKET FUNCTIONS START *** ///
public function onLap (IS_LAP $lap)
{
if (!count($this->playerData)) { return; } // ignore hotlap mode
$pos = $this->playerData[$lap->PLID]['pos'];
$this->addTime($lap->PLID, $lap->LapsDone, $this::SPLIT_LAP, $pos, $lap->ETime);
// Don't check for gap ahead if we're 1st.
if ($lap->PLID === $this->viewPlid && $this->playerData[$this->viewPlid]['pos'] > 1)
{
$plidAhead = $this->getPlidAhead($pos, $lap->LapsDone, $this::SPLIT_LAP);
// Sometimes when cars are really close to one another across a split, the SPX for the car behind in positions
// can arrive first. Don't display gap when that happens.
if ($plidAhead === null) { return; }
$this->sendGap($plidAhead, $this::GAP_AHEAD, $lap->LapsDone, $this::SPLIT_LAP);
}
// Don't check for gap behind if we're last.
if (!$this->isLastPlaceByPlid($this->viewPlid))
{
if ($this->getPlidAhead($pos, $lap->LapsDone, $this::SPLIT_LAP) == $this->viewPlid)
{
$this->sendGap($lap->PLID, $this::GAP_BEHIND, $lap->LapsDone, $this::SPLIT_LAP);
}
}
}
public function onMci (IS_MCI $mci)
{
foreach ($mci->Info as $compcar)
{
if (!$compcar->Position) { continue; }
$this->playerData[$compcar->PLID]['pos'] = $compcar->Position;
$this->playerData[$compcar->PLID]['lap'] = $compcar->Lap;
}
}
public function onNpl (IS_NPL $npl)
{
$this->playerData[$npl->PLID]['pos'] = $npl->NumP;
$this->playerData[$npl->PLID]['lap'] = 1;
}
public function onPll (IS_PLL $pll)
{
unset($this->playerData[$pll->PLID]);
}
public function onRst (IS_RST $rst)
{
$this->laps = $rst->RaceLaps;
$this->splits = $this->getNumSplitsRst($rst);
$this->playerData = array();
$this->timing = array();
}
public function onSpx (IS_SPX $spx)
{
if (!count($this->playerData)) { return; }
$pos = $this->playerData[$spx->PLID]['pos'];
$lap = $this->playerData[$spx->PLID]['lap'];
if (!$lap) { $lap++; }
$this->addTime($spx->PLID, $lap, $spx->Split, $this->playerData[$spx->PLID]['pos'], $spx->ETime);
if ($spx->PLID === $this->viewPlid && $this->playerData[$this->viewPlid]['pos'] > 1)
{
$plidAhead = $this->getPlidAhead($pos, $lap, $spx->Split);
if ($plidAhead === null) { return; }
$this->sendGap($plidAhead, $this::GAP_AHEAD, $lap, $spx->Split);
}
if (!$this->isLastPlaceByPlid($this->viewPlid))
{
if ($this->getPlidAhead($pos, $lap, $spx->Split) == $this->viewPlid)
{
$this->sendGap($spx->PLID, $this::GAP_BEHIND, $lap, $spx->Split);
}
}
}
public function onSta (IS_STA $sta)
{
$this->viewPlid = $sta->ViewPLID;
}
/// *** PACKET FUNCTIONS END *** ///
function addTime ($plid, $lap, $split, $pos, $time)
{
$entry = array
(
'plid' => $plid,
'lap' => $lap,
'split' => $split,
'pos' => $pos,
'time' => $time
);
array_push($this->timing, $entry);
}
function getGap ($a, $b, $lap, $split)
{
$plidACurTime = $this->getTime($a, $lap, $split);
$plidBCurTime = $this->getTime($b, $lap, $split);
$gap = $plidACurTime - $plidBCurTime;
return $gap;
}
function getNumSplitsRst ($rst)
{
$splits = 0;
for ($i = 3; $i > 0; $i--)
{
if ($rst->{"Split$i"} && $rst->{"Split$i"} != 65535)
{
$splits = $i;
break;
}
}
return $splits;
}
function getPlidAhead ($pos, $lap, $split)
{
$pos--;
$plid = null;
foreach ($this->timing as $entry)
{
if ($entry['pos'] == $pos && $entry['lap'] == $lap && $entry['split'] == $split) { $plid = $entry['plid']; }
}
return $plid;
}
function getTime ($plid, $lap, $split)
{
$time = 0;
foreach ($this->timing as $entry)
{
if ($entry['plid'] == $plid && $entry['lap'] == $lap && $entry['split'] == $split) { $time = $entry['time']; }
}
return $time;
}
function isLastPlaceByPlid ($plid)
{
$last = false;
if ($this->playerData[$plid]['pos'] >= count($this->playerData)) { $last = true; }
return $last;
}
function ms2str ($ms, $ahead)
{
$retval = '';
$hour = abs($ms / (1000 * 60 * 60));
if ($hour >= 1) { $retval .= strval(intval($hour)) . ':'; }
$min = ($hour - intval($hour)) * 60;
if ($hour >= 1 && $min < 1) { $retval .= '00:'; }
else if ($min >= 10) { $retval .= strval(intval($min)) . ':'; }
else if ($min > 1 && $min < 10) { $retval .= '0' . strval(intval($min)) . ':'; }
$sec = ($min - intval($min)) * 60;
$retval .= number_format($sec, 3);
if ($ms >= 0 && $ahead < 0) { $retval = "^1+$retval"; }
else if ($ms < 0 && $ahead < 0) { $retval = "^2-$retval"; }
else if ($ms >= 0 && $ahead > 0) { $retval = "^1-$retval"; }
else if ($ms < 0 && $ahead > 0) { $retval = "^2+$retval"; }
return $retval;
}
function sendGap ($plid, $mode, $lap, $split)
{
if ($split === false) { return; }
if ($this->splits === 0) { $split = $this::SPLIT_LAP; }
$curGap = $this->getGap($this->viewPlid, $plid, $lap, $split);
switch ($split)
{
case $this::SPLIT_LAP:
$split = $this->splits;
break;
case 1:
$split = $this::SPLIT_LAP;
$lap--;
break;
default:
if ($this->splits === 0) { $lap--; }
else { $split--; }
break;
}
$prevGap = $this->getGap($this->viewPlid, $plid, $lap, $split);
$gapDelta = $curGap - $prevGap;
if ($mode == $this::GAP_BEHIND) { $msg = '^3Gap delta to car behind: '; }
else { $msg = '^3Gap delta to car ahead: '; }
$timemsg = $this->ms2str($gapDelta, $mode);
$msg .= $timemsg;
IS_MSL()->Msg($msg)->Send();
}
}
?>