/**
* Copyright (C) 2009-2014 Cars and Tracks Development Project (CTDP).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.ctdp.rfdynhud.gamedata;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.ctdp.rfdynhud.editor.EditorPresets;
import net.ctdp.rfdynhud.gamedata.ProfileInfo.SpeedUnits;
import net.ctdp.rfdynhud.util.RFDHLog;
import net.ctdp.rfdynhud.util.ThreeLetterCodeManager;
/**
*
* @author Marvin Froehlich (CTDP)
*/
public abstract class VehicleScoringInfo
{
protected final ScoringInfo scoringInfo;
protected final ProfileInfo profileInfo;
protected final LiveGameData gameData;
private String oldDriverName = null;
private String originalName = null;
private String name = null;
private String nameUC = null;
private String nameShort = null;
private String nameShortUC = null;
private String nameTLC = null;
private String nameTLCUC = null;
private int nameId = 0;
private Integer nameID = null;
private short place = -1;
private int lastTLCMgrUpdateId = -1;
private String vehClass = null;
private static int nextClassId = 1;
private int classId = 0;
private Integer classID = null;
private String vehicleName = null;
private VehicleInfo vehicleInfo = null;
short placeByClass = -1;
int numVehiclesInClass = -1;
float timeBehindNextByClass = 0f;
int lapsBehindNextByClass = -1;
float timeBehindLeaderByClass = 0f;
int lapsBehindLeaderByClass = -1;
VehicleScoringInfo classLeaderVSI = null;
VehicleScoringInfo classNextInFrontVSI = null;
VehicleScoringInfo classNextBehindVSI = null;
private float lapDistance = -1f;
private int oldLap = -1;
private int lap = -1;
private int stintStartLap = -1;
private float stintLength = 0f;
private int pitState = -1;
final List<Laptime> laptimes = new ArrayList<Laptime>();
Laptime cachedFastestNormalLaptime = null;
Laptime cachedFastestHotLaptime = null;
private Laptime fastestLaptime = null;
private Laptime secondFastestLaptime = null;
Laptime oldAverageLaptime = null;
Laptime averageLaptime = null;
private Laptime editor_lastLaptime = null;
private Laptime editor_currLaptime = null;
private Laptime editor_fastestLaptime = null;
float topspeed = 0f;
float engineRPM = -1f;
float engineMaxRPM = -1f;
int engineBoostMapping = -1;
int gear = -1000;
private static final Map<String, Integer> classToIDMap = new HashMap<String, Integer>();
public final ScoringInfo getScoringInfo()
{
return ( scoringInfo );
}
public boolean isValid()
{
return ( scoringInfo.isValid() );
}
protected void resetDerivateData()
{
this.oldDriverName = name;
this.originalName = getDriverNameImpl();
this.name = originalName;
this.nameUC = null;
this.nameShort = null;
this.nameShortUC = null;
this.nameTLC = null;
this.nameTLCUC = null;
this.classLeaderVSI = null;
this.classNextInFrontVSI = null;
this.classNextBehindVSI = null;
}
/**
* Generates a <b>unique</b> ID for this vehicle.
*
* @param index the current index into the list of {@link VehicleScoringInfo}s in {@link ScoringInfo}. This index may not stay valid after initialization.
*
* @return a <b>unique</b> ID for this vehicle.
*/
protected abstract Integer refreshIDImpl( int index );
protected final Integer refreshID( int index )
{
this.nameID = refreshIDImpl( index );
this.nameId = nameID.intValue();
return ( nameID );
}
private void updateClassID()
{
String vehClass = getVehicleClass();
Integer id = classToIDMap.get( vehClass );
if ( id == null )
{
id = nextClassId++;
classToIDMap.put( vehClass, id );
}
this.classId = id.intValue();
this.classID = id;
}
void applyEditorPresets( int index, EditorPresets editorPresets )
{
if ( editorPresets == null )
return;
if ( isPlayer() )
{
oldDriverName = name;
name = editorPresets.getDriverName();
originalName = name;
//setDriverName( name );
nameUC = null;
nameShort = null;
nameShortUC = null;
nameTLC = null;
nameTLCUC = null;
nameID = refreshID( index );
nameId = nameID.intValue();
}
int lc = getLapsCompleted();
if ( lc > 0 )
{
if ( ( laptimes.size() < lc ) || ( laptimes.get( lc - 1 ) == null ) || ( laptimes.get( lc - 1 ).getSector1() != editorPresets.getLastSector1Time() ) || ( laptimes.get( lc - 1 ).getSector2() != editorPresets.getLastSector2Time( false ) || ( laptimes.get( lc - 1 ).getSector3() != editorPresets.getLastSector3Time() ) ) )
{
fastestLaptime = null;
secondFastestLaptime = null;
java.util.Random rnd = new java.util.Random( System.nanoTime() );
float ls1 = isPlayer() ? editorPresets.getLastSector1Time() : getLastSector1Impl();
float ls2 = isPlayer() ? editorPresets.getLastSector2Time( false ) : getLastSector2Impl() - getLastSector1Impl();
float ls3 = isPlayer() ? editorPresets.getLastSector3Time() : getLastLapTimeImpl() - getLastSector2Impl();
for ( int l = 1; l <= lc; l++ )
{
float s1 = ls1 + ( ( l == lc ) ? 0.0f : -0.33f * rnd.nextFloat() * 0.66f );
float s2 = ls2 + ( ( l == lc ) ? 0.0f : -0.33f * rnd.nextFloat() * 0.66f );
float s3 = ls3 + ( ( l == lc ) ? 0.0f : -0.33f * rnd.nextFloat() * 0.66f );
Laptime lt;
if ( ( l > laptimes.size() ) || ( laptimes.get( l - 1 ) == null ) )
{
lt = new Laptime( getDriverId(), l, s1, s2, s3, false, l == 1, true );
if ( l > laptimes.size() )
laptimes.add( lt );
else
laptimes.set( l - 1, lt );
}
else
{
lt = laptimes.get( l - 1 );
lt.sector1 = s1;
lt.sector2 = s2;
lt.sector3 = s3;
lt.updateLaptimeFromSectors();
}
if ( ( l == 1 ) || ( lt.getLapTime() < fastestLaptime.getLapTime() ) )
{
secondFastestLaptime = fastestLaptime;
fastestLaptime = lt;
}
}
editor_fastestLaptime = fastestLaptime;
LaptimesRecorder.calcAvgLaptime( this );
oldAverageLaptime = averageLaptime;
}
}
float cs1 = isPlayer() ? editorPresets.getCurrentSector1Time() : getCurrentSector1Impl();
float cs2 = isPlayer() ? editorPresets.getCurrentSector2Time( false ) : getCurrentSector2Impl() - getCurrentSector1Impl();
if ( laptimes.size() < lc + 1 )
{
Laptime lt = new Laptime( getDriverId(), lc + 1, cs1, cs2, -1f, false, false, false );
lt.isInLap = null;
laptimes.add( lt );
}
else
{
Laptime lt = laptimes.get( lc );
lt.sector1 = cs1;
lt.sector2 = cs2;
lt.updateLaptimeFromSectors();
}
if ( lc > 0 )
{
editor_lastLaptime = laptimes.get( lc - 1 );
editor_currLaptime = laptimes.get( lc );
}
if ( getPlace( false ) > 0 )
topspeed = editorPresets.getTopSpeed( getPlace( false ) - 1 );
}
/**
*
* @param timestamp
*/
protected void onDataUpdatedImpl( long timestamp )
{
}
/**
*
* @param timestamp
*/
protected final void onDataUpdated( long timestamp )
{
try
{
this.originalName = getDriverNameImpl();
place = -1;
lapDistance = -1f;
vehClass = null;
classId = 0;
classID = null;
vehicleName = null;
vehicleInfo = null;
oldLap = lap;
lap = getLapsCompleted() + 1;
if ( isPlayer() && gameData.getTelemetryData().isUpdatedInTimeScope() )
{
engineRPM = gameData.getTelemetryData().getEngineRPM();
engineMaxRPM = gameData.getTelemetryData().getEngineMaxRPM();
engineBoostMapping = gameData.getTelemetryData().getEngineBoostMapping();
gear = gameData.getTelemetryData().getCurrentGear();
}
else
{
engineRPM = -1f;
engineMaxRPM = -1f;
engineBoostMapping = -1;
gear = -1000;
}
onDataUpdatedImpl( timestamp );
}
catch ( Throwable t )
{
RFDHLog.exception( t );
}
}
private void updateStintLength()
{
int currentLap = getLapsCompleted() + 1; // Don't use getCurrentLap(), since it depends on stint length!
boolean isInPits = isInPits();
boolean isStanding = ( Math.abs( getScalarVelocityMPS() ) < 0.1f );
float trackPos = getNormalizedLapDistance();
if ( ( stintStartLap < 0 ) || ( isInPits && ( stintStartLap != currentLap ) && isStanding ) || ( stintStartLap > currentLap ) )
{
stintStartLap = currentLap;
}
int oldPitState = pitState;
if ( oldPitState == -1 )
{
if ( isInPits && isStanding )
pitState = 2;
else if ( isInPits )
pitState = 1;
else
pitState = 0;
}
else
{
if ( ( oldPitState == 2 ) && !isInPits )
{
stintStartLap = currentLap;
}
if ( isInPits )
{
if ( isStanding && ( oldPitState != 2 ) )
pitState = 2;
else if ( oldPitState == 0 )
pitState = 1;
}
else if ( oldPitState != 0 )
{
pitState = 0;
}
}
if ( !isPlayer() || gameData.isInCockpit() )
stintLength = currentLap - stintStartLap + trackPos;
else
stintLength = 0.0f;
}
void updateSomeData()
{
updateStintLength();
}
void resetExtrapolatedValues()
{
lapDistance = -1f;
}
private void resetSessionDerivateData()
{
stintStartLap = -1;
oldLap = -1;
laptimes.clear();
if ( laptimes.size() > 0 )
{
fastestLaptime = null;
secondFastestLaptime = null;
}
oldAverageLaptime = null;
averageLaptime = null;
editor_lastLaptime = null;
editor_currLaptime = null;
editor_fastestLaptime = null;
}
void onSessionStarted()
{
resetSessionDerivateData();
fastestLaptime = null;
secondFastestLaptime = null;
}
void onSessionEnded()
{
resetSessionDerivateData();
fastestLaptime = null;
secondFastestLaptime = null;
}
protected final String getOldDriverName()
{
return ( oldDriverName );
}
protected abstract String getDriverNameImpl();
protected final String getOriginalName()
{
return ( originalName );
}
/**
* Gets the full name of the driver driving this vehicle.
*
* @param upperCase whether the name should be in all upper case
*
* @return the full name of the driver driving this vehicle.
*/
public final String getDriverName( boolean upperCase )
{
/*
if ( name == null )
{
name = getDriverNameImpl();
}
*/
if ( upperCase )
{
if ( nameUC == null )
nameUC = name.toUpperCase();
return ( nameUC );
}
return ( name );
}
/**
* Gets the full name of the driver driving this vehicle.
*
* @return the full name of the driver driving this vehicle.
*/
public final String getDriverName()
{
return ( getDriverName( false ) );
}
/**
* Gets driver name (short form)
*
* @param upperCase whether the name should be in all upper case
*
* @return driver name (short form)
*/
public final String getDriverNameShort( boolean upperCase )
{
if ( ( nameShort == null ) || ( lastTLCMgrUpdateId < ThreeLetterCodeManager.getUpdateId() ) )
{
//String driverName = getDriverName( false );
String driverName = originalName;
nameShort = ThreeLetterCodeManager.getShortForm( driverName, getDriverID(), scoringInfo.getThreeLetterCodeGenerator() );
lastTLCMgrUpdateId = ThreeLetterCodeManager.getUpdateId();
}
if ( upperCase )
{
if ( nameShortUC == null )
nameShortUC = nameShort.toUpperCase();
return ( nameShortUC );
}
return ( nameShort );
}
/**
* Gets driver name (short form)
*
* @return driver name (short form)
*/
public final String getDriverNameShort()
{
return ( getDriverNameShort( false ) );
}
/**
* Gets driver name (three letter code)
*
* @param upperCase whether the name should be in all upper case
*
* @return driver name (three letter code)
*/
public final String getDriverNameTLC( boolean upperCase )
{
if ( ( nameTLC == null ) || ( lastTLCMgrUpdateId < ThreeLetterCodeManager.getUpdateId() ) )
{
//String driverName = getDriverName( false );
String driverName = originalName;
nameTLC = ThreeLetterCodeManager.getThreeLetterCode( driverName, getDriverID(), scoringInfo.getThreeLetterCodeGenerator() );
lastTLCMgrUpdateId = ThreeLetterCodeManager.getUpdateId();
}
if ( upperCase )
{
if ( nameTLCUC == null )
nameTLCUC = nameTLC.toUpperCase();
return ( nameTLCUC );
}
return ( nameTLC );
}
/**
* Gets driver name (three letter code)
*
* @return driver name (three letter code)
*/
public final String getDriverNameTLC()
{
return ( getDriverNameTLC( true ) );
}
/**
* Uniquely identifes this vehicle's driver. It returns the same value as {@link #getDriverID()}, but as a primitive int.
*
* @return the driver's id.
*/
public final int getDriverId()
{
return ( nameId );
}
/**
* Uniquely identifes this vehicle's driver. It returns the same value as {@link #getDriverId()}, but as an {@link Integer} instance.
*
* @return the driver's id.
*/
public final Integer getDriverID()
{
return ( nameID );
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode()
{
return ( getDriverId() );
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals( Object o )
{
if ( !( o instanceof VehicleScoringInfo ) )
return ( false );
return ( this.getDriverId() == ( (VehicleScoringInfo)o ).getDriverId() );
}
/**
* Gets the vehicle's name.
*
* @return the vehicle's name.
*/
protected abstract String getVehicleNameImpl();
/**
* Gets the vehicle's name.
*
* @return the vehicle's name.
*/
public final String getVehicleName()
{
if ( vehicleName == null )
{
vehicleName = getVehicleNameImpl();
}
return ( vehicleName );
}
public final VehicleInfo getVehicleInfo()
{
if ( vehicleInfo == null )
{
vehicleInfo = gameData.getModInfo().getVehicleInfoForDriver( this );
}
return ( vehicleInfo );
}
/**
* Gets the number of laps completed.
*
* @return the number of laps completed.
*/
public abstract short getLapsCompleted();
/**
* Gets the current lap index (one based).
*
* @return the current lap index (one based)
*/
public final short getCurrentLap()
{
if ( isInPits() && ( getStintLength() < 0.5f ) )
return ( getLapsCompleted() );
return ( (short)( getLapsCompleted() + 1 ) );
}
/**
* Gets, whether the current lap has just been stared. This is <code>true</code> for just one single time at the beginning of the lap.
*
* @return whether the current lap has just been stared
*/
public final boolean isLapJustStarted()
{
return ( lap != oldLap );
}
/**
* Gets the {@link SessionLimit} of the current session. If the session limit is defined to be LAPS,
* LAPS is returned. If it is defined to be timed, TIME is returned. Otherwise the method
* tries to guess the limit based on the average laptime.
*
* @param preference if both TIME and LAPS are possible, preference is returned.
*
* @return the {@link SessionLimit}.
*/
public final SessionLimit getSessionLimit( SessionLimit preference )
{
int maxLaps = scoringInfo.getMaxLaps();
if ( maxLaps > Integer.MAX_VALUE / 2 )
maxLaps = 0;
final float endTime = scoringInfo.getEndTime();
if ( ( maxLaps > 0 ) && ( maxLaps < 10000 ) )
{
if ( ( endTime > 0f ) && ( endTime < 999999f ) )
{
Laptime avgLaptime = getAverageLaptime();
if ( avgLaptime == null )
{
if ( preference == null )
return ( SessionLimit.LAPS );
return ( preference );
}
int timeLaps = (int)( endTime / avgLaptime.getLapTime() );
if ( timeLaps < maxLaps )
return ( SessionLimit.TIME );
}
return ( SessionLimit.LAPS );
}
if ( ( endTime > 0f ) && ( endTime < 999999f ) )
return ( SessionLimit.TIME );
return ( null );
}
/**
* Gets the {@link SessionLimit} of the current session. If the session limit is defined to be LAPS,
* LAPS is returned. If it is defined to be timed, TIME is returned. Otherwise the method
* tries to guess the limit based on the average laptime.
*
* @return the {@link SessionLimit}.
*/
public final SessionLimit getSessionLimit()
{
return ( getSessionLimit( null ) );
}
/**
* Gets the estimated max laps based on the session end time and average lap time.
* If the {@link SessionLimit} is defined to be LAPS, then max laps is known and returned.
*
* @return the estimated max laps.
*/
public final int getEstimatedMaxLaps()
{
if ( scoringInfo.getSessionType().isRace() && scoringInfo.getLeadersVehicleScoringInfo().getFinishStatus().isFinished() )
{
return ( scoringInfo.getLeadersVehicleScoringInfo().getLapsCompleted() );
}
short lapsCompleted = getLapsCompleted();
int maxLaps = scoringInfo.getMaxLaps();
if ( maxLaps > Integer.MAX_VALUE / 2 )
maxLaps = 0;
float endTime = scoringInfo.getEndTime();
if ( ( lapsCompleted == 0 ) || ( endTime < 0f ) || ( endTime > 999999f ) )
{
if ( maxLaps > 0 )
return ( maxLaps );
return ( -1 );
}
Laptime avgLaptime = getAverageLaptime();
if ( avgLaptime == null )
{
if ( maxLaps > 0 )
return ( maxLaps );
return ( -1 );
}
float restTime = endTime - getLapStartTime();
int timeLaps = lapsCompleted + (int)( restTime / avgLaptime.getLapTime() ) + 1;
if ( ( maxLaps <= 0 ) || ( timeLaps < maxLaps ) )
return ( timeLaps );
return ( maxLaps );
}
/**
* Gets the number of remaining laps (with fractions).
*
* @param maxLaps the maximum laps in the race
*
* @return the number of remaining laps.
*/
public final float getLapsRemaining( int maxLaps )
{
if ( maxLaps < 0 )
return ( -1f );
int lr = maxLaps - getLapsCompleted();
if ( getFinishStatus().isFinished() )
return ( lr );
return ( lr - getNormalizedLapDistance() );
}
/**
* Gets the current sector (1,2,3).
*
* @return the current sector.
*/
public abstract byte getSector();
/**
* Gets the current finish status.
*
* @return the current finish status.
*/
public abstract FinishStatus getFinishStatus();
/**
* Gets the current distance around track in meters at the time of the last data update.
*
* @return the current distance around track in meters.
*/
protected abstract float getLastKnownLapDistance();
/**
* Gets the current distance around track in meters.
*
* @return the current distance around track in meters.
*/
public final float getLapDistance()
{
if ( lapDistance < 0f )
{
//lapDistance = ( data.getLapDistance() + getScalarVelocityMPS() * scoringInfo.getExtrapolationTime() ) % scoringInfo.getTrackLength();
lapDistance = getLastKnownLapDistance() + getScalarVelocityMPS() * scoringInfo.getExtrapolationTime();
while ( lapDistance < 0f )
lapDistance += scoringInfo.getTrackLength();
lapDistance %= scoringInfo.getTrackLength();
}
return ( lapDistance );
}
/**
* Gets current distance around track as a fraction [0,1].
*
* @return current distance around track as a fraction [0,1].
*/
public final float getNormalizedLapDistance()
{
return ( getLapDistance() / scoringInfo.getTrackLength() );
}
/**
* Gets the lap, at which we started the current stint.
*
* @return the lap, at which we started the current stint.
*/
public final int getStintStartLap()
{
return ( stintStartLap );
}
/**
* Gets the current stint length with fractions.
*
* @return the current stint length with fractions.
*/
public final float getStintLength()
{
return ( stintLength );
}
/**
* Gets the Laptime object for the given lap.
*
* @param lap the lap
*
* @return the Laptime object for the given lap.
*/
public final Laptime getLaptime( int lap )
{
if ( ( lap < 1 ) || ( laptimes == null ) || ( lap > laptimes.size() ) )
return ( null );
return ( laptimes.get( lap - 1 ) );
}
void setFastestLaptime( Laptime laptime )
{
if ( laptime == this.fastestLaptime )
return;
if ( ( laptime == null ) || !laptime.isFinished() || ( laptime.getLapTime() < 0f ) )
this.secondFastestLaptime = null;
else
this.secondFastestLaptime = this.fastestLaptime;
this.fastestLaptime = laptime;
}
final Laptime _getFastestLaptime()
{
return ( fastestLaptime );
}
/**
* Gets this driver's fastest {@link Laptime}.
*
* @return this driver's fastest {@link Laptime}.
*/
public final Laptime getFastestLaptime()
{
if ( isPlayer() && DataCache.checkSessionType( scoringInfo ) )
{
Laptime cached = Laptime.isHotlap( gameData ) ? cachedFastestHotLaptime : cachedFastestNormalLaptime;
if ( ( cached != null ) && ( ( fastestLaptime == null ) || ( cached.getLapTime() < fastestLaptime.getLapTime() ) ) )
return ( cached );
}
return ( fastestLaptime );
}
/**
* Gets this driver's 2nd fastest {@link Laptime}.
*
* @return this driver's 2nd fastest {@link Laptime}.
*/
public final Laptime getSecondFastestLaptime()
{
return ( secondFastestLaptime );
}
/**
* Gets the average laptime of the current session excluding the last timed lap. Inlaps, outlaps and laps slower than (1.06 * fastest) are ignored.
*
* @return the average laptime or -1.
*/
public final Laptime getOldAverageLaptime()
{
return ( oldAverageLaptime );
}
/**
* Gets the average laptime of the current session. Inlaps, outlaps and laps slower than (1.06 * fastest) are ignored.
*
* @return the average laptime or -1.
*/
public final Laptime getAverageLaptime()
{
return ( averageLaptime );
}
/**
* Gets lateral position with respect to *very approximate* "center" path.
*
* @return lateral position with respect to *very approximate* "center" path.
*/
public abstract float getPathLateral();
/**
* Gets track edge (w.r.t. "center" path) on same side of track as vehicle.
*
* @return track edge (w.r.t. "center" path) on same side of track as vehicle.
*/
public abstract float getTrackEdge();
/**
* Gets the best sector 1 time. This is not necessarily the sector time of the best lap.
*
* @return the best sector 1 time.
*/
protected abstract float getBestSector1Impl();
/**
* Gets the best sector 1 time. This is not necessarily the sector time of the best lap.
*
* @return the best sector 1 time.
*/
public final float getBestSector1()
{
if ( editor_fastestLaptime != null )
return ( editor_fastestLaptime.getSector1() );
return ( getBestSector1Impl() );
}
/**
* Gets the best sector 2 time including sector1. This is not necessarily the sector time of the best lap.
*
* @return the best sector 2 time.
*/
protected abstract float getBestSector2Impl();
/**
* Gets the best sector 2 time. This is not necessarily the sector time of the best lap.
*
* @param includingSector1 return sum of sector1 and 2?
*
* @return the best sector 2 time.
*/
public final float getBestSector2( boolean includingSector1 )
{
if ( editor_fastestLaptime != null )
return ( editor_fastestLaptime.getSector2( includingSector1 ) );
float sec2 = getBestSector2Impl();
if ( !includingSector1 && ( sec2 > 0f ) )
sec2 -= getBestSector1();
return ( sec2 );
}
/**
* Gets the best lap time best lap time.
*
* @return the best lap time best lap time.
*/
protected abstract float getBestLapTimeImpl();
/**
* Gets the best lap time best lap time.
*
* @return the best lap time best lap time.
*/
public final float getBestLapTime()
{
if ( editor_fastestLaptime != null )
return ( editor_fastestLaptime.getLapTime() );
return ( getBestLapTimeImpl() );
}
/**
* Gets the best sector 3 time excluding sector 1 and 2. This is not necessarily the sector time of the best lap.
*
* @return the best sector 3 time.
*/
public float getBestSector3()
{
if ( editor_fastestLaptime != null )
return ( editor_fastestLaptime.getSector3() );
float lt = getBestLapTime();
if ( lt > 0f )
lt -= getBestSector2( true );
return ( lt );
}
/**
* Gets the last sector 1 time.
*
* @return the last sector 1 time.
*/
protected abstract float getLastSector1Impl();
/**
* Gets the last sector 1 time.
*
* @return the last sector 1 time.
*/
public final float getLastSector1()
{
if ( editor_lastLaptime != null )
return ( editor_lastLaptime.getSector1() );
return ( getLastSector1Impl() );
}
/**
* Gets the last sector 2 time including sector 1.
*
* @return the last sector 2 time.
*/
protected abstract float getLastSector2Impl();
/**
* Gets the last sector 2 time.
*
* @param includingSector1 return sum of sector1 and 2?
*
* @return the last sector 2 time.
*/
public final float getLastSector2( boolean includingSector1 )
{
if ( editor_lastLaptime != null )
return ( editor_lastLaptime.getSector2( includingSector1 ) );
float sec2 = getLastSector2Impl();
if ( !includingSector1 )
sec2 -= getLastSector1();
return ( sec2 );
}
/**
* Gets the last lap time.
*
* @return the last lap time.
*/
protected abstract float getLastLapTimeImpl();
/**
* Gets the last lap time.
*
* @return the last lap time.
*/
public final float getLastLapTime()
{
if ( editor_lastLaptime != null )
return ( editor_lastLaptime.getLapTime() );
return ( getLastLapTimeImpl() );
}
/**
* Gets the last lap time.
*
* @return the last lap time.
*/
public final Laptime getLastLaptime()
{
if ( editor_lastLaptime != null )
return ( editor_lastLaptime );
return ( getLaptime( getLapsCompleted() ) );
}
/**
* Gets the last sector 3 time.
*
* @return the last sector 3 time.
*/
public final float getLastSector3()
{
if ( editor_lastLaptime != null )
return ( editor_lastLaptime.getSector3() );
return ( getLastLapTime() - getLastSector2( true ) );
}
/**
* Gets the current sector 1 (if valid).
*
* @return the current sector 1 (if valid)
*/
protected abstract float getCurrentSector1Impl();
/**
* Gets the current sector 1 (if valid).
*
* @return the current sector 1 (if valid)
*/
public final float getCurrentSector1()
{
if ( editor_currLaptime != null )
return ( editor_currLaptime.getSector1() );
return ( getCurrentSector1Impl() );
}
/**
* Gets current sector 2 time including sector 1.
*
* @return current sector 2 time.
*/
protected abstract float getCurrentSector2Impl();
/**
* Gets current sector 2 time.
*
* @param includingSector1 only affects result if sector1 is valid
*
* @return current sector 2 time.
*/
public final float getCurrentSector2( boolean includingSector1 )
{
if ( editor_currLaptime != null )
return ( editor_currLaptime.getSector2( includingSector1 ) );
float sec2 = getCurrentSector2Impl();
if ( !includingSector1 && ( sec2 > 0f ) )
sec2 -= getCurrentSector1();
return ( sec2 );
}
/**
* The current laptime (may be incomplete).
*
* @return current laptime.
*/
public final float getCurrentLaptime()
{
/*
if ( getStintLength() < 1.0f )
return ( -1f );
*/
if ( !scoringInfo.getSessionType().isRace() && ( getStintLength() < 1.0f ) )
return ( -1f );
return ( scoringInfo.getSessionTime() - getLapStartTime() );
}
/**
* Gets the number of pitstops made.
*
* @return the number of pitstops made.
*/
public abstract short getNumPitstopsMade();
/**
* Gets the number of scheduled pitstops (only valid for the player).
*
* @return the number of scheduled pitstops.
*/
public short getNumberOfScheduledPitstops()
{
if ( !isPlayer() )
return ( -1 );
return ( gameData.getTelemetryData().getNumberOfScheduledPitstops() );
}
/**
* Gets the number of outstanding penalties.
*
* @return the number of outstanding penalties.
*/
public abstract short getNumOutstandingPenalties();
/**
* @return is this the player's vehicle?
*/
public abstract boolean isPlayer();
/**
* @return who's in control?
*/
public abstract VehicleControl getVehicleControl();
/**
* between pit entrance and pit exit (not always accurate for remote vehicles)
*
* @return is this vehicle in the pit lane?
*/
public abstract boolean isInPits();
/**
* Gets the number of vehicles in the same vehicle class.
*
* @return the number of vehicles in the same vehicle class.
*/
public final int getNumVehiclesInSameClass()
{
scoringInfo.updateClassScoring();
return ( numVehiclesInClass );
}
/**
* 1-based position
*
* @return 1-based position
*/
protected abstract short getPlaceImpl();
/**
* 1-based position
*
* @param byClass only consider vehicles in the same class
*
* @return 1-based position
*/
public final short getPlace( boolean byClass )
{
if ( byClass )
{
scoringInfo.updateClassScoring();
return ( placeByClass );
}
if ( place < 0 )
{
place = getPlaceImpl();
}
return ( place );
}
/**
* Gets the {@link VehicleScoringInfo}, that leads the same class.
*
* @return the {@link VehicleScoringInfo}, that leads the same class.
*/
public final VehicleScoringInfo getLeaderByClass()
{
scoringInfo.updateClassScoring();
return ( classLeaderVSI );
}
/**
* Gets the {@link VehicleScoringInfo}, that is next in front.
*
* @param byClass only consider vehicles in the same class
*
* @return the {@link VehicleScoringInfo}, that is next in front.
*/
public final VehicleScoringInfo getNextInFront( boolean byClass )
{
if ( byClass )
{
scoringInfo.updateClassScoring();
return ( classNextInFrontVSI );
}
short place = getPlace( false );
if ( place <= 1 )
return ( null );
return ( scoringInfo.getVehicleScoringInfo( place - 2 ) );
}
/**
* Gets the {@link VehicleScoringInfo}, that is next behind.
*
* @param byClass only consider vehicles in the same class
*
* @return the {@link VehicleScoringInfo}, that is next behind.
*/
public final VehicleScoringInfo getNextBehind( boolean byClass )
{
if ( byClass )
{
scoringInfo.updateClassScoring();
return ( classNextBehindVSI );
}
short place = getPlace( false );
if ( place >= scoringInfo.getNumVehicles() )
return ( null );
return ( scoringInfo.getVehicleScoringInfo( place + 0 ) );
}
/**
* Gets the vehicle class.
*
* @return the vehicle class.
*/
protected abstract String getVehicleClassImpl();
/**
* Gets the vehicle class.
*
* @return the vehicle class.
*/
public final String getVehicleClass()
{
if ( vehClass == null )
{
vehClass = getVehicleClassImpl();
}
return ( vehClass );
}
void setVehicleClass( String vehClass )
{
this.vehClass = vehClass;
}
/**
* Gets the vehicle class id.
*
* @return the vehicle class id.
*/
public final int getVehicleClassId()
{
if ( classId <= 0 )
{
updateClassID();
}
return ( classId );
}
/**
* Gets the vehicle class id.
*
* @return the vehicle class id.
*/
public final Integer getVehicleClassID()
{
if ( classID == null )
{
updateClassID();
}
return ( classID );
}
/**
* Gets the time behind vehicle in next higher place.
*
* @return the time behind vehicle in next higher place.
*/
protected abstract float getTimeBehindNextInFrontImpl();
/**
* Gets the time behind vehicle in next higher place.
*
* @param byClass only consider vehicles in the same class
*
* @return the time behind vehicle in next higher place.
*/
public final float getTimeBehindNextInFront( boolean byClass )
{
if ( byClass )
{
scoringInfo.updateClassScoring();
return ( timeBehindNextByClass );
}
return ( getTimeBehindNextInFrontImpl() );
}
/**
* Gets the laps behind vehicle in next higher place.
*
* @return the laps behind vehicle in next higher place.
*/
protected abstract int getLapsBehindNextInFrontImpl();
/**
* Gets the laps behind vehicle in next higher place.
*
* @param byClass only consider vehicles in the same class
*
* @return the laps behind vehicle in next higher place.
*/
public final int getLapsBehindNextInFront( boolean byClass )
{
if ( byClass )
{
scoringInfo.updateClassScoring();
return ( lapsBehindNextByClass );
}
return ( getLapsBehindNextInFrontImpl() );
}
/**
* Gets the time behind leader.
*
* @return the time behind leader.
*/
protected abstract float getTimeBehindLeaderImpl();
/**
* Gets the time behind leader.
*
* @param byClass only consider vehicles in the same class
*
* @return the time behind leader.
*/
public final float getTimeBehindLeader( boolean byClass )
{
if ( byClass )
{
scoringInfo.updateClassScoring();
return ( timeBehindLeaderByClass );
}
return ( getTimeBehindLeaderImpl() );
}
/**
* Gets the laps behind leader.
*
* @return the laps behind leader.
*/
protected abstract int getLapsBehindLeaderImpl();
/**
* Gets the laps behind leader.
*
* @param byClass only consider vehicles in the same class
*
* @return the laps behind leader.
*/
public final int getLapsBehindLeader( boolean byClass )
{
if ( byClass )
{
scoringInfo.updateClassScoring();
return ( lapsBehindLeaderByClass );
}
return ( getLapsBehindNextInFrontImpl() );
}
/**
* Gets the time this lap was started at.
*
* @return the time this lap was started at.
*/
public abstract float getLapStartTime();
/**
* Gets world position in meters.
*
* @param position output buffer
*/
public abstract void getWorldPosition( TelemVect3 position );
/**
* Gets world position in meters.
*
* @return world position in meters.
*/
public abstract float getWorldPositionX();
/**
* Gets world position in meters.
*
* @return world position in meters.
*/
public abstract float getWorldPositionY();
/**
* Gets world position in meters.
*
* @return world position in meters.
*/
public abstract float getWorldPositionZ();
/**
* Gets the current engine RPM.<br />
* This is only valid, if set by a {@link LiveGameDataController}.
*
* @return the current engine RPM or -1, if unknown.
*/
public final float getEngineRPM()
{
return ( engineRPM );
}
/**
* Gets the current engine' max RPM.<br />
* This is only valid, if set by a {@link LiveGameDataController}.
*
* @return the current engine max RPM or -1, if unknown.
*/
public final float getEngineMaxRPM()
{
return ( engineMaxRPM );
}
/**
* Gets the current engine boost mapping.<br />
* This is only valid, if set by a {@link LiveGameDataController}.
*
* @return the current engine boost mapping or -1, if unknown.
*/
public final int getEngineBoostMapping()
{
return ( engineBoostMapping );
}
/**
* Gets the current gear.<br />
* This is only valid, if set by a {@link LiveGameDataController}.
*
* @return the current gear or -1000, if unknown.
*/
public int getCurrentGear()
{
return ( gear );
}
/**
* velocity (meters/sec) in local vehicle coordinates
*
* @param localVel output buffer
*/
public abstract void getLocalVelocity( TelemVect3 localVel );
/**
* Gets velocity (meters/sec) in local vehicle coordinates.
*
* @return velocity (meters/sec) in local vehicle coordinates.
*/
public abstract float getScalarVelocityMS();
/**
* @deprecated replaced by {@link #getScalarVelocityMS()}.
*
* @return velocity (meters/sec) in local vehicle coordinates.
*/
@Deprecated
public final float getScalarVelocityMPS()
{
return ( getScalarVelocityMS() );
}
/**
* Gets velocity (mi/h).
*
* @return velocity (mi/h).
*/
public final float getScalarVelocityMih()
{
float mps = getScalarVelocityMS();
return ( mps * SpeedUnits.Convert.MS_TO_MIH );
}
/**
* @deprecated replaced by {@link #getScalarVelocityMih()}.
*
* @return velocity (mi/h).
*/
@Deprecated
public final float getScalarVelocityMPH()
{
return ( getScalarVelocityMih() );
}
/**
* Gets velocity (km/h).
*
* @return velocity (km/h).
*/
public final float getScalarVelocityKmh()
{
float mps = getScalarVelocityMS();
return ( mps * SpeedUnits.Convert.MS_TO_KMH );
}
/**
* @deprecated replaced by {@link #getScalarVelocityKmh()}.
*
* @return velocity (km/h).
*/
@Deprecated
public final float getScalarVelocityKPH()
{
return ( getScalarVelocityKmh() );
}
/**
* Gets velocity in the units selected in the PLR.
*
* @return velocity in the units selected in the PLR.
*/
public final float getScalarVelocity()
{
if ( profileInfo.getSpeedUnits() == SpeedUnits.MIH )
return ( getScalarVelocityMih() );
return ( getScalarVelocityKmh() );
}
/**
* Gets topspeed in km/h.
*
* @return topspeed in km/h.
*/
public final float getTopspeed()
{
return ( topspeed );
}
/**
* acceleration (meters/sec^2) in local vehicle coordinates
*
* @param localAccel output buffer
*/
public abstract void getLocalAcceleration( TelemVect3 localAccel );
/**
* top row of orientation matrix (also converts local vehicle vectors into world X using dot product)
*
* @param oriX output buffer
*/
public abstract void getOrientationX( TelemVect3 oriX );
/**
* mid row of orientation matrix (also converts local vehicle vectors into world Y using dot product)
*
* @param oriY output buffer
*/
public abstract void getOrientationY( TelemVect3 oriY );
/**
* bot row of orientation matrix (also converts local vehicle vectors into world Z using dot product)
*
* @param oriZ output buffer
*/
public abstract void getOrientationZ( TelemVect3 oriZ );
/**
* rotation (radians/sec) in local vehicle coordinates
*
* @param localRot output buffer
*/
public abstract void getLocalRotation( TelemVect3 localRot );
/**
* rotational acceleration (radians/sec^2) in local vehicle coordinates
*
* @param localRotAccel output buffer
*/
public abstract void getLocalRotationalAcceleration( TelemVect3 localRotAccel );
/**
* Gets the current vehicle's pit state.
*
* @return the current vehicle's pit state or <code>null</code>, if unknown.
*/
public abstract PitState getPitState();
/**
* Gets the status flag, currently shown to this vehicle.
*
* @return the status flag, currently shown to this vehicle or <code>null</code>, if unknown.
*/
public abstract StatusFlag getStatusFlag();
/**
* Gets a description of the currently installed vehicle upgrade pack.
*
* @return a description of the currently installed vehicle upgrade pack or <code>null</code>, if unknown.
*/
public abstract VehicleUpgradePack getUpgradePack();
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
if ( !isValid() )
return ( this.getClass().getSimpleName() + " (invalid)" );
return ( this.getClass().getSimpleName() + " (\"" + getDriverName() + "\", " + getDriverId() + ")" );
}
protected VehicleScoringInfo( ScoringInfo scoringInfo, ProfileInfo profileInfo, LiveGameData gameData )
{
this.scoringInfo = scoringInfo;
this.profileInfo = profileInfo;
this.gameData = gameData;
}
/**
* Comparator to sort by place.
*
* @author Marvin Froehlich
*/
public static final class VSIPlaceComparator implements Comparator<VehicleScoringInfo>
{
/**
* Singleton instance
*/
public static final VSIPlaceComparator INSTANCE = new VSIPlaceComparator();
@Override
public int compare( VehicleScoringInfo vsi1, VehicleScoringInfo vsi2 )
{
if ( vsi1.getPlace( false ) < vsi2.getPlace( false ) )
return ( -1 );
if ( vsi1.getPlace( false ) > vsi2.getPlace( false ) )
return ( +1 );
return ( 0 );
}
private VSIPlaceComparator()
{
}
}
}