/**
* 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.widgets.standard.fuel;
import java.awt.Font;
import java.io.IOException;
import net.ctdp.rfdynhud.gamedata.LiveGameData;
import net.ctdp.rfdynhud.gamedata.ProfileInfo.MeasurementUnits;
import net.ctdp.rfdynhud.gamedata.ScoringInfo;
import net.ctdp.rfdynhud.gamedata.SessionLimit;
import net.ctdp.rfdynhud.gamedata.TelemetryData;
import net.ctdp.rfdynhud.gamedata.VehicleScoringInfo;
import net.ctdp.rfdynhud.input.InputAction;
import net.ctdp.rfdynhud.properties.BooleanProperty;
import net.ctdp.rfdynhud.properties.ColorProperty;
import net.ctdp.rfdynhud.properties.FactoredIntProperty;
import net.ctdp.rfdynhud.properties.FontProperty;
import net.ctdp.rfdynhud.properties.ImageProperty;
import net.ctdp.rfdynhud.properties.IntProperty;
import net.ctdp.rfdynhud.properties.PosSizeProperty;
import net.ctdp.rfdynhud.properties.Position;
import net.ctdp.rfdynhud.properties.PropertiesContainer;
import net.ctdp.rfdynhud.properties.Property;
import net.ctdp.rfdynhud.properties.PropertyLoader;
import net.ctdp.rfdynhud.properties.Size;
import net.ctdp.rfdynhud.render.DrawnString;
import net.ctdp.rfdynhud.render.DrawnString.Alignment;
import net.ctdp.rfdynhud.render.DrawnStringFactory;
import net.ctdp.rfdynhud.render.ImageTemplate;
import net.ctdp.rfdynhud.render.TextureImage2D;
import net.ctdp.rfdynhud.render.TransformableTexture;
import net.ctdp.rfdynhud.util.FontUtils;
import net.ctdp.rfdynhud.util.NumberUtil;
import net.ctdp.rfdynhud.util.PropertyWriter;
import net.ctdp.rfdynhud.util.SubTextureCollector;
import net.ctdp.rfdynhud.valuemanagers.Clock;
import net.ctdp.rfdynhud.valuemanagers.IntervalManager;
import net.ctdp.rfdynhud.values.AbstractSize;
import net.ctdp.rfdynhud.values.IntValue;
import net.ctdp.rfdynhud.values.LongValue;
import net.ctdp.rfdynhud.values.RelativePositioning;
import net.ctdp.rfdynhud.values.ValidityTest;
import net.ctdp.rfdynhud.widgets.base.widget.Widget;
import net.ctdp.rfdynhud.widgets.standard._util.StandardWidgetSet;
/**
* The {@link FuelWidget} displays fuel information like current fuel load, fuel usage per lap,
* and computed fuel for scheduled pitstops.
*
* @author Marvin Froehlich (CTDP)
*/
public class FuelWidget extends Widget
{
private static final InputAction INPUT_ACTION_INC_PITSTOP_LAP = new InputAction( "IncPitstopLapAction" );
private static final InputAction INPUT_ACTION_DEC_PITSTOP_LAP = new InputAction( "DecPitstopLapAction" );
private static final InputAction INPUT_ACTION_INC_PITSTOP_FUEL = new InputAction( "IncPitstopFuelAction" );
private static final InputAction INPUT_ACTION_DEC_PITSTOP_FUEL = new InputAction( "DecPitstopFuelAction" );
private final FontProperty font2 = new FontProperty( "font2", StandardWidgetSet.STANDARD_FONT3.getKey() );
private final BooleanProperty displayFuelBar = new BooleanProperty( "displayFuelBar", true );
private final BooleanProperty displayTankSize = new BooleanProperty( "displayTankSize", true );
private final BooleanProperty displayFuelLoad = new BooleanProperty( "displayFuelLoad", true );
private final BooleanProperty displayFuelWeight = new BooleanProperty( "displayFuelWeight", true );
private final BooleanProperty displayFuelLaps = new BooleanProperty( "displayFuelLaps", true );
private final BooleanProperty displayFuelUsage = new BooleanProperty( "displayFuelUsage", true );
private final BooleanProperty displayPitstopInfo = new BooleanProperty( "displayPitstopInfo", true );
private final BooleanProperty horizontalFuelBar = new BooleanProperty( "horizontalFuelBar", "horizFuelBar", false );
private final Size fuelBarWidth;
private final ImageProperty fuelBarImage = new ImageProperty( "fuelBarImage", null, "", false, true );
private TextureImage2D fuelBarTexture = null;
private final ColorProperty fuelBarBackgroundColor = new ColorProperty( "fuelBarBackgroundColor", "fuelBarBGColor", "#000000" );
private final ColorProperty fuelBarColor = new ColorProperty( "fuelBarColor", "#54760B" );
private final FontProperty tankSizeFont = new FontProperty( "tankSizeFont", FontUtils.getFontString( "Monospaced", Font.PLAIN, 9, true, false ) );
private final FontProperty fuelFont = new FontProperty( "fuelFont", FontProperty.STANDARD_FONT.getKey() );
private final ColorProperty fuelFontColor = new ColorProperty( "fuelFontColor", "#FFFFFFCD" );
private final BooleanProperty roundUpRemainingLaps = new BooleanProperty( "roundUpRemainingLaps", "roundUpRemLaps", false );
private final ImageProperty lowFuelWarningImageNameOff = new ImageProperty( "lowFuelWarningImageOff", "imageOff", "standard/shiftlight_off.png", false, true )
{
@Override
protected void onValueChanged( String oldValue, String newValue )
{
lowFuelWarningImageOff = null;
}
};
private TransformableTexture lowFuelWarningImageOff = null;
private final ImageProperty lowFuelWarningImageNameOn = new ImageProperty( "lowFuelWarningImageOn", "imageOn", "standard/shiftlight_on_red.png", false, true )
{
@Override
protected void onValueChanged( String oldValue, String newValue )
{
lowFuelWarningImageOn = null;
}
};
private TransformableTexture lowFuelWarningImageOn = null;
private final AbstractSize lowFuelWarnImgSize = new AbstractSize()
{
@Override
public int getEffectiveWidth()
{
loadLowFuelWarningImages( null );
if ( lowFuelWarningImageOn == null )
return ( 0 );
return ( lowFuelWarningImageOn.getWidth() );
}
@Override
public int getEffectiveHeight()
{
loadLowFuelWarningImages( null );
if ( lowFuelWarningImageOn == null )
return ( 0 );
return ( lowFuelWarningImageOn.getHeight() );
}
};
private final Position lowFuelWarningImagePosition = Position.newLocalPosition( this, RelativePositioning.TOP_RIGHT, 4.0f, false, 4.0f, false, lowFuelWarnImgSize );
private final Property lowFuelWarningImagePositionPositioningProperty = lowFuelWarningImagePosition.getPositioningProperty( "lowFuelWarningImagePositioning", "imagePositioning" );
private final PosSizeProperty lowFuelWarningImagePositionXProperty = lowFuelWarningImagePosition.getXProperty( "lowFuelWarningImagePositionX", "imagePosX" );
private final PosSizeProperty lowFuelWarningImagePositionYProperty = lowFuelWarningImagePosition.getYProperty( "lowFuelWarningImagePositionY", "imagePosY" );
private final Size lowFuelWarningImageSize = Size.newLocalSize( this, 20.0f, true, 20.0f, true );
//private final PosSizeProperty lowFuelWarningImageWidthProperty = lowFuelWarningImageSize.getWidthProperty( "lowFuelWarningImageWidth", "imageWidth" );
private final PosSizeProperty lowFuelWarningImageHeightProperty = lowFuelWarningImageSize.getHeightProperty( "lowFuelWarningImageHeight", "imageHeight" );
private final IntProperty lowFuelWarningLaps = new IntProperty( "lowFuelWarningLaps", "laps", 1, 1, 10, false );
private final FactoredIntProperty lowFuelBlinkTime = new FactoredIntProperty( "lowFuelBlinkTime", "blinkTime", 1000000, 0, 500, 0, 5000 );
private final IntervalManager lowFuelBlinkManager = new IntervalManager( lowFuelBlinkTime );
private DrawnString fuelLoadString0 = null;
private DrawnString fuelLoadString1 = null;
private DrawnString fuelLoadString2 = null;
private DrawnString fuelLoadString3 = null;
private DrawnString fuelUsageHeaderString = null;
private DrawnString fuelUsageLastLapHeaderString = null;
private DrawnString fuelUsageAvgHeaderString = null;
private DrawnString fuelUsageLastLapString = null;
private DrawnString fuelUsageAvgString = null;
private DrawnString nextPitstopHeaderString = null;
private DrawnString nextPitstopLapString = null;
private DrawnString nextPitstopFuelString = null;
private int oldNextPitstopLapCorrection = -1;
private int nextPitstopLapCorrection = 0;
private int nextPitstopFuelLapsCorrection = 0;
private int nextPitstopFuelLapsCorrection2 = 0;
private final IntValue pitstopFuel = new IntValue( ValidityTest.GREATER_THAN, 0 );
private final IntValue stintLengthV = new IntValue( ValidityTest.GREATER_THAN, 0 );
private final LongValue fuelUsage = new LongValue();
private int oldFuel = -1;
private float oldAverage = -1f;
public FuelWidget()
{
super( StandardWidgetSet.INSTANCE, StandardWidgetSet.WIDGET_PACKAGE, 17.8f, true, 13.5f, true );
this.fuelBarWidth = Size.newLocalSize( this, 26.f, true, 0f, true );
getFontProperty().setFont( "StandardFont2" );
setPadding( 4, 4, 4, 4 );
}
/**
* {@inheritDoc}
*/
@Override
public void saveProperties( PropertyWriter writer ) throws IOException
{
super.saveProperties( writer );
writer.writeProperty( font2, "The used (smaller) font." );
writer.writeProperty( displayFuelBar, "Render the fuel bar?" );
writer.writeProperty( displayTankSize, "Display the tank size information?" );
writer.writeProperty( displayFuelLoad, "Display fuel load information?" );
writer.writeProperty( displayFuelWeight, "Display fuel weight information?" );
writer.writeProperty( displayFuelLaps, "Display fuel load in laps?" );
writer.writeProperty( displayFuelUsage, "Display fuel usage information?" );
writer.writeProperty( displayPitstopInfo, "Display pitstop calculation information?" );
writer.writeProperty( horizontalFuelBar, "Whether to render the fuel bar as a horizontal instead of a vertical bar." );
writer.writeProperty( fuelBarImage, "An image to paint the fuel bar from." );
writer.writeProperty( fuelBarBackgroundColor, "The color used for the fuel bar's background." );
writer.writeProperty( fuelBarColor, "The color used for the fuel bar." );
writer.writeProperty( tankSizeFont, "The used font for max fuel load (tank size)." );
writer.writeProperty( fuelFont, "The used font for fuel load." );
writer.writeProperty( fuelFontColor, "The color to use for fuel load in the format #RRGGBB (hex)." );
writer.writeProperty( roundUpRemainingLaps, "Round up remaining fuel laps to include the current lap?" );
writer.writeProperty( lowFuelWarningImageNameOff, "Image name for the off-state of the low fuel warning." );
writer.writeProperty( lowFuelWarningImageNameOn, "Image name for the on-state of the low fuel warning." );
writer.writeProperty( lowFuelWarningImagePositionPositioningProperty, "Positioning type for the low-fuel-warning image." );
writer.writeProperty( lowFuelWarningImagePositionXProperty, "X-position for the low-fuel-warning image." );
writer.writeProperty( lowFuelWarningImagePositionYProperty, "Y-position for the low-fuel-warning image." );
//writer.writeProperty( lowFuelWarningImageWidthProperty, "Width for the low-fuel-warning image." );
writer.writeProperty( lowFuelWarningImageHeightProperty, "Height for the low-fuel-warning image." );
writer.writeProperty( lowFuelWarningLaps, "Number of laps to start warning before out of fuel." );
writer.writeProperty( lowFuelBlinkTime, "Blink time in milli seconds for low fuel warning (0 to disable)." );
}
/**
* {@inheritDoc}
*/
@Override
public void loadProperty( PropertyLoader loader )
{
super.loadProperty( loader );
if ( loader.loadProperty( font2 ) );
else if ( loader.loadProperty( displayFuelBar ) );
else if ( loader.loadProperty( displayTankSize ) );
else if ( loader.loadProperty( displayFuelLoad ) );
else if ( loader.loadProperty( displayFuelWeight ) );
else if ( loader.loadProperty( displayFuelLaps ) );
else if ( loader.loadProperty( displayFuelUsage ) );
else if ( loader.loadProperty( displayPitstopInfo ) );
else if ( loader.loadProperty( horizontalFuelBar ) );
else if ( loader.loadProperty( fuelBarImage ) );
else if ( loader.loadProperty( fuelBarBackgroundColor ) );
else if ( loader.loadProperty( fuelBarColor ) );
else if ( loader.loadProperty( tankSizeFont ) );
else if ( loader.loadProperty( fuelFont ) );
else if ( loader.loadProperty( fuelFontColor ) );
else if ( loader.loadProperty( roundUpRemainingLaps ) );
else if ( loader.loadProperty( lowFuelWarningImageNameOff ) );
else if ( loader.loadProperty( lowFuelWarningImageNameOn ) );
else if ( loader.loadProperty( lowFuelWarningImagePositionPositioningProperty ) );
else if ( loader.loadProperty( lowFuelWarningImagePositionXProperty ) );
else if ( loader.loadProperty( lowFuelWarningImagePositionYProperty ) );
//else if ( loader.loadProperty( lowFuelWarningImageWidthProperty ) );
else if ( loader.loadProperty( lowFuelWarningImageHeightProperty ) );
else if ( loader.loadProperty( lowFuelWarningLaps ) );
else if ( loader.loadProperty( lowFuelBlinkTime ) );
}
/**
* {@inheritDoc}
*/
@Override
protected void addFontPropertiesToContainer( PropertiesContainer propsCont, boolean forceAll )
{
super.addFontPropertiesToContainer( propsCont, forceAll );
propsCont.addProperty( font2 );
}
/**
* {@inheritDoc}
*/
@Override
public void getProperties( PropertiesContainer propsCont, boolean forceAll )
{
super.getProperties( propsCont, forceAll );
propsCont.addGroup( "Optional Content" );
propsCont.addProperty( displayFuelBar );
propsCont.addProperty( displayTankSize );
propsCont.addProperty( displayFuelLoad );
propsCont.addProperty( displayFuelWeight );
propsCont.addProperty( displayFuelLaps );
propsCont.addProperty( displayFuelUsage );
propsCont.addProperty( displayPitstopInfo );
propsCont.addGroup( "Fuel Bar" );
propsCont.addProperty( horizontalFuelBar );
propsCont.addProperty( fuelBarImage );
propsCont.addProperty( fuelBarBackgroundColor );
propsCont.addProperty( fuelBarColor );
propsCont.addProperty( tankSizeFont );
propsCont.addProperty( fuelFont );
propsCont.addProperty( fuelFontColor );
propsCont.addProperty( roundUpRemainingLaps );
propsCont.addGroup( "Low Fuel Warning" );
propsCont.addProperty( lowFuelWarningImageNameOff );
propsCont.addProperty( lowFuelWarningImageNameOn );
propsCont.addProperty( lowFuelWarningImagePositionPositioningProperty );
propsCont.addProperty( lowFuelWarningImagePositionXProperty );
propsCont.addProperty( lowFuelWarningImagePositionYProperty );
//propsCont.addProperty( lowFuelWarningImageWidthProperty );
propsCont.addProperty( lowFuelWarningImageHeightProperty );
propsCont.addProperty( lowFuelWarningLaps );
propsCont.addProperty( lowFuelBlinkTime );
}
/**
* {@inheritDoc}
*/
@Override
public InputAction[] getInputActions()
{
return ( new InputAction[] { INPUT_ACTION_INC_PITSTOP_LAP, INPUT_ACTION_DEC_PITSTOP_LAP, INPUT_ACTION_INC_PITSTOP_FUEL, INPUT_ACTION_DEC_PITSTOP_FUEL } );
}
private final boolean getDrawFuelBar()
{
return ( displayFuelBar.getBooleanValue() || displayTankSize.getBooleanValue() || displayFuelLoad.getBooleanValue() || displayFuelWeight.getBooleanValue() || displayFuelLaps.getBooleanValue() );
}
private final boolean isLowFuelWaningUsed()
{
return ( lowFuelBlinkManager.isUsed() && !lowFuelWarningImageNameOn.isNoImage() );
}
private void resetBlink( boolean isEditorMode )
{
lowFuelBlinkManager.reset();
if ( lowFuelWarningImageOff != null )
{
lowFuelWarningImageOff.setTranslation( lowFuelWarningImagePosition.getEffectiveX(), lowFuelWarningImagePosition.getEffectiveY() );
lowFuelWarningImageOff.setVisible( false );
}
if ( lowFuelWarningImageOn != null )
{
lowFuelWarningImageOn.setTranslation( lowFuelWarningImagePosition.getEffectiveX(), lowFuelWarningImagePosition.getEffectiveY() );
lowFuelWarningImageOn.setVisible( isEditorMode );
}
}
private void loadLowFuelWarningImages( Boolean isEditorMode )
{
if ( !isLowFuelWaningUsed() )
{
lowFuelWarningImageOff = null;
lowFuelWarningImageOn = null;
return;
}
boolean offVisible = ( lowFuelWarningImageOff == null ) ? false : lowFuelWarningImageOff.isVisible();
boolean onVisible = ( lowFuelWarningImageOn == null ) ? false : lowFuelWarningImageOn.isVisible();
boolean offReloaded = false;
if ( !lowFuelWarningImageNameOff.isNoImage() )
{
ImageTemplate it = lowFuelWarningImageNameOff.getImage();
int h = lowFuelWarningImageSize.getEffectiveHeight();
int w = Math.round( h * it.getBaseAspect() );
boolean isEditorMode2;
if ( isEditorMode == null )
{
if ( lowFuelWarningImageOff == null )
isEditorMode2 = false;
else
isEditorMode2 = ( lowFuelWarningImageOff.getTexture().getWidth() != lowFuelWarningImageOff.getTexture().getMaxWidth() ) || ( lowFuelWarningImageOff.getTexture().getHeight() != lowFuelWarningImageOff.getTexture().getMaxHeight() );
}
else
{
isEditorMode2 = isEditorMode.booleanValue();
}
if ( ( lowFuelWarningImageOff == null ) || ( lowFuelWarningImageOff.getWidth() != w ) || ( lowFuelWarningImageOff.getHeight() != h ) )
{
lowFuelWarningImageOff = it.getScaledTransformableTexture( w, h, lowFuelWarningImageOff, isEditorMode2 );
offReloaded = true;
}
}
boolean onReloaded = false;
if ( !lowFuelWarningImageNameOn.isNoImage() )
{
ImageTemplate it = lowFuelWarningImageNameOn.getImage();
int h = lowFuelWarningImageSize.getEffectiveHeight();
int w = Math.round( h * it.getBaseAspect() );
boolean isEditorMode2;
if ( isEditorMode == null )
{
if ( lowFuelWarningImageOn == null )
isEditorMode2 = false;
else
isEditorMode2 = ( lowFuelWarningImageOn.getTexture().getWidth() != lowFuelWarningImageOn.getTexture().getMaxWidth() ) || ( lowFuelWarningImageOn.getTexture().getHeight() != lowFuelWarningImageOn.getTexture().getMaxHeight() );
}
else
{
isEditorMode2 = isEditorMode.booleanValue();
}
if ( ( lowFuelWarningImageOn == null ) || ( lowFuelWarningImageOn.getWidth() != w ) || ( lowFuelWarningImageOn.getHeight() != h ) )
{
lowFuelWarningImageOn = it.getScaledTransformableTexture( w, h, lowFuelWarningImageOn, isEditorMode2 );
onReloaded = true;
}
}
if ( offReloaded && ( lowFuelWarningImageOff != null ) )
{
lowFuelWarningImageOff.setTranslation( lowFuelWarningImagePosition.getEffectiveX(), lowFuelWarningImagePosition.getEffectiveY() );
lowFuelWarningImageOff.setVisible( offVisible );
}
if ( onReloaded && ( lowFuelWarningImageOn != null ) )
{
lowFuelWarningImageOn.setTranslation( lowFuelWarningImagePosition.getEffectiveX(), lowFuelWarningImagePosition.getEffectiveY() );
lowFuelWarningImageOn.setVisible( onVisible );
}
}
/**
* {@inheritDoc}
*/
@Override
public void onCockpitEntered( LiveGameData gameData, boolean isEditorMode )
{
super.onCockpitEntered( gameData, isEditorMode );
loadLowFuelWarningImages( isEditorMode );
resetBlink( isEditorMode );
this.nextPitstopLapCorrection = 0;
this.nextPitstopFuelLapsCorrection = 0;
this.nextPitstopFuelLapsCorrection2 = 0;
this.oldNextPitstopLapCorrection = ( ( nextPitstopLapCorrection + Short.MAX_VALUE / 2 ) << 16 ) | ( nextPitstopFuelLapsCorrection + Short.MAX_VALUE / 2 );
this.pitstopFuel.reset();
this.stintLengthV.reset();
this.fuelUsage.reset();
this.oldFuel = -1;
this.oldAverage = -1f;
}
/**
* {@inheritDoc}
*/
@Override
public void onPitsExited( LiveGameData gameData, boolean isEditorMode )
{
super.onPitsExited( gameData, isEditorMode );
if ( stintLengthV.getValue() < 1 )
{
this.nextPitstopLapCorrection = 0;
this.nextPitstopFuelLapsCorrection = 0;
this.nextPitstopFuelLapsCorrection2 = 0;
}
this.stintLengthV.reset();
}
/**
* {@inheritDoc}
*/
@Override
protected Boolean onVehicleControlChanged( VehicleScoringInfo viewedVSI, LiveGameData gameData, boolean isEditorMode )
{
super.onVehicleControlChanged( viewedVSI, gameData, isEditorMode );
return ( viewedVSI.isPlayer() );
}
/**
* {@inheritDoc}
*/
@Override
protected Boolean onBoundInputStateChanged( InputAction action, boolean state, int modifierMask, long when, LiveGameData gameData, boolean isEditorMode )
{
if ( action == INPUT_ACTION_INC_PITSTOP_LAP )
{
nextPitstopFuelLapsCorrection += nextPitstopFuelLapsCorrection2;
nextPitstopFuelLapsCorrection2 = 0;
if ( nextPitstopLapCorrection < 0 )
{
this.nextPitstopLapCorrection++;
this.nextPitstopFuelLapsCorrection--;
}
}
else if ( action == INPUT_ACTION_DEC_PITSTOP_LAP )
{
nextPitstopFuelLapsCorrection += nextPitstopFuelLapsCorrection2;
nextPitstopFuelLapsCorrection2 = 0;
this.nextPitstopLapCorrection--;
this.nextPitstopFuelLapsCorrection++;
}
else if ( action == INPUT_ACTION_INC_PITSTOP_FUEL )
{
nextPitstopFuelLapsCorrection += nextPitstopFuelLapsCorrection2;
nextPitstopFuelLapsCorrection2 = 0;
this.nextPitstopFuelLapsCorrection++;
}
else if ( action == INPUT_ACTION_DEC_PITSTOP_FUEL )
{
nextPitstopFuelLapsCorrection += nextPitstopFuelLapsCorrection2;
nextPitstopFuelLapsCorrection2 = 0;
this.nextPitstopFuelLapsCorrection--;
}
return ( null );
}
/**
* {@inheritDoc}
*/
@Override
protected void initSubTextures( LiveGameData gameData, boolean isEditorMode, int widgetInnerWidth, int widgetInnerHeight, SubTextureCollector collector )
{
if ( !isLowFuelWaningUsed() )
return;
loadLowFuelWarningImages( isEditorMode );
if ( lowFuelWarningImageOff != null )
collector.add( lowFuelWarningImageOff );
if ( lowFuelWarningImageOn != null )
collector.add( lowFuelWarningImageOn );
}
private static final String getFuelUnits( MeasurementUnits measurementUnits )
{
if ( measurementUnits == MeasurementUnits.IMPERIAL )
return ( Loc.fuel_units_IMPERIAL );
return ( Loc.fuel_units_METRIC );
}
private void initFuelBarTexture( boolean isEditorMode, int width, int height )
{
if ( displayFuelBar.getBooleanValue() )
{
ImageTemplate it = fuelBarImage.getImage();
if ( it == null )
{
fuelBarTexture = null;
return;
}
if ( horizontalFuelBar.getBooleanValue() )
fuelBarTexture = it.getScaledTextureImage( width, height * 2, fuelBarTexture, isEditorMode );
else
fuelBarTexture = it.getScaledTextureImage( width * 2, height, fuelBarTexture, isEditorMode );
}
else
{
fuelBarTexture = null;
}
}
/**
* {@inheritDoc}
*/
@Override
protected void initialize( LiveGameData gameData, boolean isEditorMode, DrawnStringFactory dsf, TextureImage2D texture, int width, int height )
{
final java.awt.Font font = getFont();
final boolean fontAntiAliased = isFontAntiAliased();
final java.awt.Font font2 = this.font2.getFont();
final boolean font2AntiAliased = this.font2.isAntiAliased();
final java.awt.Color fontColor = getFontColor();
final java.awt.Font tankSizeFont = this.tankSizeFont.getFont();
final boolean tankSizeFontAntiAliased = this.tankSizeFont.isAntiAliased();
final java.awt.Font fuelFont = this.fuelFont.getFont();
final boolean fuelFontAntiAliased = this.fuelFont.isAntiAliased();
final java.awt.Color fuelFontColor = this.fuelFontColor.getColor();
boolean showAnyAdditionalText = displayFuelUsage.getBooleanValue() || displayPitstopInfo.getBooleanValue();
int fuelBarWidth = showAnyAdditionalText ? this.fuelBarWidth.getEffectiveWidth() : width;
int fuelBarCenter = showAnyAdditionalText ? ( fuelBarWidth / 2 ) : ( width / 2 );
initFuelBarTexture( isEditorMode, fuelBarWidth, height );
DrawnString relY = null;
if ( displayTankSize.getBooleanValue() )
{
fuelLoadString0 = dsf.newDrawnString( "fuelLoadString0", null, relY, fuelBarCenter, 0, Alignment.CENTER, false, tankSizeFont, tankSizeFontAntiAliased, fuelFontColor, null, null );
relY = fuelLoadString0;
}
else
{
fuelLoadString0 = null;
}
if ( displayFuelLoad.getBooleanValue() )
{
fuelLoadString1 = dsf.newDrawnString( "fuelLoadString1", null, relY, fuelBarCenter, 0, Alignment.CENTER, false, fuelFont, fuelFontAntiAliased, fuelFontColor, null, getFuelUnits( gameData.getProfileInfo().getMeasurementUnits() ) );
relY = fuelLoadString1;
}
else
{
fuelLoadString1 = null;
}
if ( displayFuelWeight.getBooleanValue() )
{
fuelLoadString2 = dsf.newDrawnString( "fuelLoadString2", null, relY, fuelBarCenter, 0, Alignment.CENTER, false, fuelFont, fuelFontAntiAliased, fuelFontColor, null, Loc.fuelLoad2_postfix );
relY = fuelLoadString2;
}
else
{
fuelLoadString2 = null;
}
if ( displayFuelLaps.getBooleanValue() )
{
fuelLoadString3 = dsf.newDrawnString( "fuelLoadString3", null, relY, fuelBarCenter, 0, Alignment.CENTER, false, font2, font2AntiAliased, fuelFontColor, null, null );
relY = fuelLoadString3;
}
else
{
fuelLoadString3 = null;
}
int textTop = -2;
int textLeft = fuelBarWidth + 4;
if ( !displayFuelBar.getBooleanValue() && !displayTankSize.getBooleanValue() && !displayFuelLoad.getBooleanValue() && !displayFuelWeight.getBooleanValue() && !displayFuelLaps.getBooleanValue() )
textLeft = 0;
boolean b = displayFuelUsage.getBooleanValue();
//if ( displayFuelUsage.getBooleanValue() )
{
int lastToAvgSpacing = 75; // 85
fuelUsageHeaderString = dsf.newDrawnStringIf( b, "fuelUsageHeaderString", null, null, textLeft, textTop, Alignment.LEFT, false, font, fontAntiAliased, fontColor, Loc.fuelUsageHeader + ":", null );
fuelUsageLastLapHeaderString = dsf.newDrawnStringIf( b, "fuelUsageLastLapHeaderString", null, fuelUsageHeaderString, textLeft + 50, 2, Alignment.CENTER, false, font, fontAntiAliased, fontColor, Loc.fuelUsageLastLapHeader, null );
fuelUsageAvgHeaderString = dsf.newDrawnStringIf( b, "fuelUsageAvgHeaderString", null, fuelUsageHeaderString, textLeft + 50 + lastToAvgSpacing, 2, Alignment.CENTER, false, font, fontAntiAliased, fontColor, Loc.fuelUsageAvgHeader, null );
fuelUsageLastLapString = dsf.newDrawnStringIf( b, "fuelUsageOneLapString", null, fuelUsageLastLapHeaderString, textLeft + 50, 2, Alignment.CENTER, false, font, fontAntiAliased, fontColor );
fuelUsageAvgString = dsf.newDrawnStringIf( b, "fuelUsageAvgString", null, fuelUsageAvgHeaderString, textLeft + 50 + lastToAvgSpacing, 2, Alignment.CENTER, false, font, fontAntiAliased, fontColor );
}
b = displayPitstopInfo.getBooleanValue();
//if ( displayPitstopInfo.getBooleanValue() )
{
int psOffsetY = displayFuelUsage.getBooleanValue() ? 7 : textTop;
nextPitstopHeaderString = dsf.newDrawnStringIf( b, "nextPitstopHeaderString", null, fuelUsageLastLapString, textLeft, psOffsetY, Alignment.LEFT, false, font, fontAntiAliased, fontColor, Loc.nextPitstopHeader + ":", null );
nextPitstopLapString = dsf.newDrawnStringIf( b, "nextPitstopLapString", null, nextPitstopHeaderString, textLeft + 10, 2, Alignment.LEFT, false, font2, font2AntiAliased, fontColor, Loc.nextPitstopLap_prefix + ": ", null );
nextPitstopFuelString = dsf.newDrawnStringIf( b, "nextPitstopFuelString", null, nextPitstopLapString, textLeft + 10, 0, Alignment.LEFT, false, font2, font2AntiAliased, fontColor, Loc.nextPitstopFuel_prefix + ": ", null );
}
resetBlink( isEditorMode );
}
private boolean drawFuelBar( float fuel, int tankSize, TextureImage2D texture, int offsetX, int offsetY, int x, int y, int height )
{
final boolean displayFuelBar = this.displayFuelBar.getBooleanValue();
boolean showAnyAdditionalText = displayFuelUsage.getBooleanValue() || displayPitstopInfo.getBooleanValue();
int w = showAnyAdditionalText ? this.fuelBarWidth.getEffectiveWidth() : getInnerSize().getEffectiveWidth();
int h = height;
boolean areaDrawn = false;
if ( horizontalFuelBar.getBooleanValue() )
{
int barWidth = displayFuelBar ? Math.min( (int)( w * fuel / tankSize ), w ) : 0;
if ( !displayFuelBar || ( barWidth < w ) )
{
if ( fuelBarTexture != null )
{
if ( fuelBarTexture.hasAlphaChannel() )
clearBackgroundRegion( texture, offsetX, offsetY, x, y, w, h, false, null );
texture.drawImage( fuelBarTexture, 0, 0, fuelBarTexture.getWidth(), fuelBarTexture.getHeight() / 2, offsetX + x, offsetY + y, w, h, false, null );
areaDrawn = true;
}
else if ( !getBackground().valueEquals( fuelBarBackgroundColor.getColor() ) && fuelBarBackgroundColor.hasVisibleColor() )
{
if ( fuelBarBackgroundColor.getColor().getAlpha() < 255 )
clearBackgroundRegion( texture, offsetX, offsetY, x + barWidth, y, w - barWidth, h, false, null );
texture.fillRectangle( fuelBarBackgroundColor.getColor(), offsetX + x + barWidth, offsetY + y, w - barWidth, h, false, null );
areaDrawn = true;
}
}
if ( displayFuelBar )
{
if ( fuelBarTexture != null )
{
texture.drawImage( fuelBarTexture, 0, fuelBarTexture.getHeight() / 2, barWidth, fuelBarTexture.getHeight() / 2, offsetX + x, offsetY + y, barWidth, h, false, null );
areaDrawn = true;
}
else
{
if ( fuelBarColor.getColor().getAlpha() < 255 )
clearBackgroundRegion( texture, offsetX, offsetY, x, y, barWidth, h, false, null );
texture.fillRectangle( fuelBarColor.getColor(), offsetX + x, offsetY + y, barWidth, h, false, null );
areaDrawn = true;
}
}
}
else
{
int barHeight = displayFuelBar ? Math.min( (int)( h * fuel / tankSize ), h ) : 0;
if ( !displayFuelBar || ( barHeight < h ) )
{
if ( fuelBarTexture != null )
{
if ( fuelBarTexture.hasAlphaChannel() )
clearBackgroundRegion( texture, offsetX, offsetY, x, y, w, h, false, null );
texture.drawImage( fuelBarTexture, 0, 0, fuelBarTexture.getWidth() / 2, fuelBarTexture.getHeight(), offsetX + x, offsetY + y, w, h, false, null );
areaDrawn = true;
}
else if ( !getBackground().valueEquals( fuelBarBackgroundColor.getColor() ) && fuelBarBackgroundColor.hasVisibleColor() )
{
if ( fuelBarBackgroundColor.getColor().getAlpha() < 255 )
clearBackgroundRegion( texture, offsetX, offsetY, x, y, w, h - barHeight, false, null );
texture.fillRectangle( fuelBarBackgroundColor.getColor(), offsetX + x, offsetY + y, w, h - barHeight, false, null );
areaDrawn = true;
}
}
if ( displayFuelBar )
{
if ( fuelBarTexture != null )
{
texture.drawImage( fuelBarTexture, fuelBarTexture.getWidth() / 2, 0, fuelBarTexture.getWidth() / 2, barHeight, offsetX + x, offsetY + y, w, barHeight, false, null );
areaDrawn = true;
}
else
{
if ( fuelBarColor.getColor().getAlpha() < 255 )
clearBackgroundRegion( texture, offsetX, offsetY, x, y + h - barHeight, w, barHeight, false, null );
texture.fillRectangle( fuelBarColor.getColor(), offsetX + x, offsetY + y + h - barHeight, w, barHeight, false, null );
areaDrawn = true;
}
}
}
texture.markDirty( offsetX + x, offsetY + y, w, h, null );
return ( areaDrawn );
}
@Override
protected void drawWidget( Clock clock, boolean needsCompleteRedraw, LiveGameData gameData, boolean isEditorMode, TextureImage2D texture, int offsetX, int offsetY, int width, int height )
{
ScoringInfo scoringInfo = gameData.getScoringInfo();
TelemetryData telemData = gameData.getTelemetryData();
VehicleScoringInfo vsi = scoringInfo.getPlayersVehicleScoringInfo();
MeasurementUnits measurementUnits = gameData.getProfileInfo().getMeasurementUnits();
final int tankSize = (int)gameData.getPhysics().getFuelRange().getMaxValue();
if ( needsCompleteRedraw )
{
//fuelHeaderString.draw( offsetX, offsetY, String.valueOf( tankSize ) + " " + ( measurementUnits == MeasurementUnits.IMPERIAL ? Loc.fuelHeader_postfix_IMPERIAL : Loc.fuelHeader_postfix_METRIC ), backgroundColor, texture );
if ( displayFuelUsage.getBooleanValue() )
{
fuelUsageHeaderString.draw( offsetX, offsetY, "", texture );
fuelUsageLastLapHeaderString.draw( offsetX, offsetY, "", texture );
fuelUsageAvgHeaderString.draw( offsetX, offsetY, "", texture );
}
if ( displayPitstopInfo.getBooleanValue() )
{
//nextPitstopHeaderString.draw( offsetX, offsetY, "", backgroundColor, image );
}
}
final float fuel = telemData.getFuel();
final float fuelL = telemData.getFuelL();
final float avgFuelUsage = telemData.getFuelUsageAverageL();
final float lastFuelUsage = telemData.getFuelUsageLastLap();
final float stintLength = !isEditorMode ? vsi.getStintLength() : 5.2f;
if ( isEditorMode )
{
if ( lowFuelWarningImageOn != null )
{
lowFuelWarningImageOn.setVisible( true );
if ( lowFuelWarningImageOff != null )
{
lowFuelWarningImageOff.setVisible( false );
}
}
else if ( lowFuelWarningImageOff != null )
{
lowFuelWarningImageOff.setVisible( false );
}
}
else if ( lowFuelBlinkManager.isUsed() && ( lowFuelWarningImageOn != null ) )
{
boolean warn = false;
if ( avgFuelUsage > 0f )
{
float halfLiter = gameData.getProfileInfo().getMeasurementUnits().getFuelAmountFromLiters( 0.5f );
float lapsForFuel = ( ( fuel - halfLiter ) / avgFuelUsage ) + ( stintLength - (int)stintLength );
int maxLaps = scoringInfo.getEstimatedMaxLaps( vsi );
if ( maxLaps < 0 )
maxLaps = 999999;
int lapsRemaining = maxLaps - vsi.getLapsCompleted();
warn = ( lapsForFuel < 1.05f + lowFuelWarningLaps.getIntValue() ) && ( lapsForFuel < lapsRemaining );
}
if ( warn )
{
lowFuelBlinkManager.update( scoringInfo.getSessionNanos() );
}
else
{
lowFuelBlinkManager.reset();
}
if ( lowFuelWarningImageOff != null )
lowFuelWarningImageOff.setVisible( !lowFuelBlinkManager.getState() );
lowFuelWarningImageOn.setVisible( lowFuelBlinkManager.getState() );
}
int fuel_ = Math.round( fuel * 10f );
if ( needsCompleteRedraw || ( clock.c() && ( ( fuel_ != oldFuel ) || ( avgFuelUsage != oldAverage ) ) ) )
{
oldFuel = fuel_;
oldAverage = avgFuelUsage;
int fuelY = 0;
int fuelHeight = height;
boolean fuelBarDrawn = getDrawFuelBar();
if ( fuelBarDrawn )
fuelBarDrawn = drawFuelBar( fuel, tankSize, texture, offsetX, offsetY, 0, fuelY, fuelHeight );
if ( displayTankSize.getBooleanValue() )
fuelLoadString0.draw( offsetX, offsetY + fuelY, "(" + String.valueOf( tankSize ) + getFuelUnits( gameData.getProfileInfo().getMeasurementUnits() ) + ")", texture, !fuelBarDrawn );
if ( displayFuelLoad.getBooleanValue() )
fuelLoadString1.draw( offsetX, offsetY + fuelY, NumberUtil.formatFloat( fuel, 1, true ), texture, !fuelBarDrawn );
if ( displayFuelWeight.getBooleanValue() )
fuelLoadString2.draw( offsetX, offsetY + fuelY, NumberUtil.formatFloat( fuelL * gameData.getPhysics().getWeightOfOneLiterOfFuel(), 1, true ), texture, !fuelBarDrawn );
if ( displayFuelLaps.getBooleanValue() )
{
String string;
if ( avgFuelUsage > 0f )
{
float halfLiter = gameData.getProfileInfo().getMeasurementUnits().getFuelAmountFromLiters( 0.5f );
float fuelLaps = (float)Math.floor( ( fuel - halfLiter ) * 10 / avgFuelUsage ) / 10f;
fuelLaps = Math.max( 0f, fuelLaps );
if ( roundUpRemainingLaps.getBooleanValue() )
string = NumberUtil.formatFloat( fuelLaps + ( stintLength - (int)stintLength ), 1, true ) + Loc.fuelLoad3_postfix;
else
string = NumberUtil.formatFloat( fuelLaps, 1, true ) + Loc.fuelLoad3_postfix;
}
else
{
string = Loc.fuelLoad3_na;
}
fuelLoadString3.draw( offsetX, offsetY + fuelY, string, texture, !fuelBarDrawn );
}
}
stintLengthV.update( (int)stintLength );
if ( displayFuelUsage.getBooleanValue() )
{
fuelUsage.update( ( (long)Float.floatToIntBits( lastFuelUsage ) << 32 ) | Float.floatToIntBits( avgFuelUsage ) );
if ( needsCompleteRedraw || ( clock.c() && fuelUsage.hasChanged() ) )
{
if ( avgFuelUsage < 0f )
{
String string;
if ( lastFuelUsage > 0f )
string = NumberUtil.formatFloat( lastFuelUsage, 2, true ) + getFuelUnits( measurementUnits );
else
string = Loc.fuelUsageLastLap_na;
fuelUsageLastLapString.draw( offsetX, offsetY, string, texture );
fuelUsageAvgString.draw( offsetX, offsetY, Loc.fuelUsageAvg_na, texture );
}
else
{
String string;
if ( lastFuelUsage > 0f )
string = NumberUtil.formatFloat( lastFuelUsage, 2, true ) + getFuelUnits( measurementUnits );
else
string = Loc.fuelUsageLastLap_na;
fuelUsageLastLapString.draw( offsetX, offsetY, string, texture );
string = NumberUtil.formatFloat( telemData.getFuelUsageAverage(), 2, true ) + getFuelUnits( measurementUnits );
fuelUsageAvgString.draw( offsetX, offsetY, string, texture );
}
}
}
if ( displayPitstopInfo.getBooleanValue() )
{
if ( needsCompleteRedraw )
{
nextPitstopHeaderString.draw( offsetX, offsetY, "", texture );
}
int nextPitstopLap = -1;
int pitstopFuel_ = -1;
int pitstopLaps = -1;
int maxLaps = scoringInfo.getEstimatedMaxLaps( vsi );
if ( maxLaps > 0 )
{
maxLaps -= vsi.getLapsBehindLeader( false );
if ( vsi.getSessionLimit() == SessionLimit.TIME )
maxLaps++; // In a timed race we never know, if we might be fast enough to drive another lap.
}
if ( isEditorMode )
{
nextPitstopLap = 31;
pitstopLaps = (int)( 72 / avgFuelUsage );
}
else if ( avgFuelUsage > 0f )
{
int currLap = vsi.getCurrentLap();
int remainingFuelLaps = (int)Math.floor( ( fuel / avgFuelUsage ) + ( stintLength - (int)stintLength ) );
nextPitstopLap = vsi.getLapsCompleted() + remainingFuelLaps + nextPitstopLapCorrection;
if ( nextPitstopLap < currLap )
{
int delta = currLap - nextPitstopLap;
nextPitstopLapCorrection += delta;
nextPitstopFuelLapsCorrection -= delta;
nextPitstopLap = vsi.getLapsCompleted() + remainingFuelLaps + nextPitstopLapCorrection;
}
nextPitstopFuelLapsCorrection2 = 0;
int nextPitstopIndex = Math.min( gameData.getScoringInfo().getPlayersVehicleScoringInfo().getNumPitstopsMade() + 1, gameData.getSetup().getGeneral().getNumPitstops() );
float pitstopFuel0 = gameData.getSetup().getGeneral().getFuel( nextPitstopIndex );
pitstopLaps = (int)Math.floor( pitstopFuel0 / avgFuelUsage );
pitstopFuel_ = (int)Math.ceil( ( pitstopLaps + nextPitstopFuelLapsCorrection + nextPitstopFuelLapsCorrection2 ) * avgFuelUsage );
while ( pitstopFuel_ < avgFuelUsage )
{
nextPitstopFuelLapsCorrection2++;
pitstopFuel_ = (int)Math.ceil( ( pitstopLaps + nextPitstopFuelLapsCorrection + nextPitstopFuelLapsCorrection2 ) * avgFuelUsage );
}
while ( ( pitstopFuel_ > tankSize ) || ( scoringInfo.getSessionType().isRace() && ( maxLaps > 0 ) && ( nextPitstopLap + pitstopLaps + nextPitstopFuelLapsCorrection + nextPitstopFuelLapsCorrection2 > maxLaps ) ) )
{
nextPitstopFuelLapsCorrection2--;
pitstopFuel_ = (int)Math.ceil( ( pitstopLaps + nextPitstopFuelLapsCorrection + nextPitstopFuelLapsCorrection2 ) * avgFuelUsage );
}
}
if ( isEditorMode )
pitstopFuel.update( 72 );
else
pitstopFuel.update( pitstopFuel_ );
int tmp = ( ( nextPitstopLapCorrection + Short.MAX_VALUE / 2 ) << 16 ) | ( nextPitstopFuelLapsCorrection + nextPitstopFuelLapsCorrection2 + Short.MAX_VALUE / 2 );
if ( needsCompleteRedraw || ( tmp != oldNextPitstopLapCorrection ) || pitstopFuel.hasChanged() )
{
oldNextPitstopLapCorrection = tmp;
if ( ( avgFuelUsage > 0f ) && ( maxLaps > 0 ) )
{
if ( pitstopFuel.isValid() )
{
String string = String.valueOf( nextPitstopLap ) + " (" + NumberUtil.delta( nextPitstopLapCorrection ) + ")";
nextPitstopLapString.draw( offsetX, offsetY, string, texture );
string = String.valueOf( pitstopFuel.getValue() + (int)Math.ceil( avgFuelUsage * 0.25f ) ) + getFuelUnits( measurementUnits ) + " (" + ( pitstopLaps + nextPitstopFuelLapsCorrection + nextPitstopFuelLapsCorrection2 ) + Loc.nextPitstopFuel_laps + "," + NumberUtil.delta( nextPitstopFuelLapsCorrection ) + ")";
nextPitstopFuelString.draw( offsetX, offsetY, string, texture );
}
else
{
nextPitstopLapString.draw( offsetX, offsetY, Loc.nextPitstopLap_enough, texture );
nextPitstopFuelString.draw( offsetX, offsetY, Loc.nextPitstopFuel_enough, texture );
}
}
else
{
nextPitstopLapString.draw( offsetX, offsetY, Loc.nextPitstopLap_na + " (" + NumberUtil.delta( nextPitstopLapCorrection ) + ")", texture );
nextPitstopFuelString.draw( offsetX, offsetY, Loc.nextPitstopFuel_na + " (" + NumberUtil.delta( nextPitstopFuelLapsCorrection + nextPitstopFuelLapsCorrection2 ) + " " + Loc.nextPitstopFuel_laps + ")", texture );
}
}
}
}
}