/** * 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.devicestates; import java.io.IOException; import java.util.ArrayList; import java.util.List; import net.ctdp.rfdynhud.gamedata.DeviceLegalStatus; import net.ctdp.rfdynhud.gamedata.IgnitionStatus; import net.ctdp.rfdynhud.gamedata.LiveGameData; import net.ctdp.rfdynhud.gamedata.TelemetryData; import net.ctdp.rfdynhud.gamedata.VehicleScoringInfo; import net.ctdp.rfdynhud.properties.BooleanProperty; import net.ctdp.rfdynhud.properties.EnumProperty; import net.ctdp.rfdynhud.properties.FloatProperty; import net.ctdp.rfdynhud.properties.ImagePropertyWithTexture; import net.ctdp.rfdynhud.properties.IntProperty; import net.ctdp.rfdynhud.properties.ListProperty; import net.ctdp.rfdynhud.properties.ListProperty.ListPropertyValue; import net.ctdp.rfdynhud.properties.PropertiesContainer; import net.ctdp.rfdynhud.properties.PropertyLoader; import net.ctdp.rfdynhud.render.DrawnStringFactory; import net.ctdp.rfdynhud.render.TextureImage2D; import net.ctdp.rfdynhud.util.PropertyWriter; import net.ctdp.rfdynhud.util.SubTextureCollector; import net.ctdp.rfdynhud.valuemanagers.Clock; import net.ctdp.rfdynhud.values.BoolValue; import net.ctdp.rfdynhud.values.EnumValue; import net.ctdp.rfdynhud.values.IntValue; import net.ctdp.rfdynhud.widgets.base.widget.Widget; import net.ctdp.rfdynhud.widgets.standard._util.StandardWidgetSet; /** * The {@link DeviceStatesWidget} displays icons for the current states of different devices. * * TODO: Check, whether the used car actually has a speed limiter. * * @author Marvin Froehlich (CTDP) */ public class DeviceStatesWidget extends Widget { private static class DeviceStateValue implements ListPropertyValue { final Integer state; final String stateString; final String caption; @Override public String toString() { return ( caption ); } @Override public String getForConfigFile() { return ( stateString ); } @Override public boolean parse( String valueFromConfigFile ) { return ( stateString.equals( valueFromConfigFile ) ); } DeviceStateValue( Integer state, String stateString, String caption ) { this.state = state; this.stateString = stateString; this.caption = caption; } static final DeviceStateValue never = new DeviceStateValue( null, "N", "Never" ); static final DeviceStateValue always = new DeviceStateValue( null, "A", "Always" ); } private static enum Alignment { TOP_LEFT, CENTER, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, ; } private final FloatProperty columns = new FloatProperty( "columns", 2, 1, Integer.MAX_VALUE ); private final IntProperty gap = new IntProperty( "gap", 5 ); private final EnumProperty<Alignment> alignment = new EnumProperty<Alignment>( "alignment", Alignment.TOP_LEFT ); private ListProperty<DeviceStateValue, List<DeviceStateValue>> speedLimiterVisibility = null; private final BooleanProperty blinkSpeedLimiter = new BooleanProperty( "blinkSpeedLimiter", true ); private ListProperty<DeviceStateValue, List<DeviceStateValue>> ignitionVisibility = null; private ListProperty<DeviceStateValue, List<DeviceStateValue>> frontFlapVisibility = null; private ListProperty<DeviceStateValue, List<DeviceStateValue>> rearFlapVisibility = null; private final ImagePropertyWithTexture imageBackgroundImageOff = new ImagePropertyWithTexture( "iconBackgroundOff", null, "standard/drivingaid_background_off.png", false, true ); private final ImagePropertyWithTexture imageBackgroundImageLow = new ImagePropertyWithTexture( "iconBackgroundLow", null, "standard/drivingaid_background_low.png", false, true ); private final ImagePropertyWithTexture imageBackgroundImageMedium = new ImagePropertyWithTexture( "iconBackgroundMedium", null, "standard/drivingaid_background_medium.png", false, true ); private final ImagePropertyWithTexture imageBackgroundImageHigh = new ImagePropertyWithTexture( "iconBackgroundHigh", null, "standard/drivingaid_background_high.png", false, true ); private final ImagePropertyWithTexture imageSpeedLimiterOff = new ImagePropertyWithTexture( "imageSpeedLimiterOff", "speedLimiterOff", "standard/speed-limiter-deactivated.png", false, false ); private final ImagePropertyWithTexture imageSpeedLimiterOn = new ImagePropertyWithTexture( "imageSpeedLimiterOn", "speedLimiterOn", "standard/speed-limiter-activated.png", false, false ); private final ImagePropertyWithTexture imageIgnitionOff = new ImagePropertyWithTexture( "imageIgnitionOff", "ignitionOff", "standard/ignition-off.png", false, false ); private final ImagePropertyWithTexture imageIgnitionOn = new ImagePropertyWithTexture( "imageIgnitionOn", "ignitionOn", "standard/ignition-on.png", false, false ); private final ImagePropertyWithTexture imageIgnitionStarter = new ImagePropertyWithTexture( "imageIgnitionStarter", "ignitionStarter", "standard/ignition-starter.png", false, false ); private final ImagePropertyWithTexture imageFrontFlapDisallowed = new ImagePropertyWithTexture( "imageFrontFlapDisallowed", "frontFlapDisallowed", "standard/front-flap-disallowed.png", false, false ); private final ImagePropertyWithTexture imageFrontFlapNotYetAllowed = new ImagePropertyWithTexture( "imageFrontFlapNotYetAllowed", "frontFlapNotYetAllowed", "standard/front-flap-not-yet-allowed.png", false, false ); private final ImagePropertyWithTexture imageFrontFlapAllowed = new ImagePropertyWithTexture( "imageFrontFlapAllowed", "frontFlapAllowed", "standard/front-flap-allowed.png", false, false ); private final ImagePropertyWithTexture imageFrontFlapActivated = new ImagePropertyWithTexture( "imageFrontFlapActivated", "frontFlapActivated", "standard/front-flap-activated.png", false, false ); private final ImagePropertyWithTexture imageRearFlapDisallowed = new ImagePropertyWithTexture( "imageRearFlapDisallowed", "rearFlapDisallowed", "standard/rear-flap-disallowed.png", false, false ); private final ImagePropertyWithTexture imageRearFlapNotYetAllowed = new ImagePropertyWithTexture( "imageRearFlapNotYetAllowed", "rearFlapNotYetAllowed", "standard/rear-flap-not-yet-allowed.png", false, false ); private final ImagePropertyWithTexture imageRearFlapAllowed = new ImagePropertyWithTexture( "imageRearFlapAllowed", "rearFlapAllowed", "standard/rear-flap-allowed.png", false, false ); private final ImagePropertyWithTexture imageRearFlapActivated = new ImagePropertyWithTexture( "imageRearFlapActivated", "rearFlapActivated", "standard/rear-flap-activated.png", false, false ); private int iconSize = 0; private int innerIconSize = 0; private int innerIconOffset = 0; private final BoolValue speedLimiterValue = new BoolValue(); private final BoolValue speedLimiterDspValue = new BoolValue(); private final EnumValue<IgnitionStatus> ignitionValue = new EnumValue<IgnitionStatus>(); private final IntValue frontFlapValue = new IntValue(); private final IntValue rearFlapValue = new IntValue(); private int numRows = -1; private static final long BLINK_DELAY = 500000000L; // 500 ms, 2 Hz private static final long BLINK_INIT_VALUE = -( Long.MAX_VALUE / 2L ); private long nextBlinkTime = BLINK_INIT_VALUE; private boolean nextBlinkVis = true; public DeviceStatesWidget() { super( StandardWidgetSet.INSTANCE, StandardWidgetSet.WIDGET_PACKAGE, 8.125f, 34.417f ); getBorderProperty().setBorder( null ); getBackgroundProperty().setColorValue( "#00000000" ); setPadding( 0 ); } /** * {@inheritDoc} */ @Override public void saveProperties( PropertyWriter writer ) throws IOException { super.saveProperties( writer ); writer.writeProperty( columns, "Number of columns to display" ); writer.writeProperty( gap, "Gap between the icons" ); writer.writeProperty( alignment, "Alignment of icons" ); if ( speedLimiterVisibility != null ) writer.writeProperty( speedLimiterVisibility, "Speed limiter visibility" ); writer.writeProperty( blinkSpeedLimiter, "Whether the speed limiter icon will blink, if it should be enabled/disabled." ); if ( ignitionVisibility != null ) writer.writeProperty( ignitionVisibility, "Ignition visibility" ); if ( frontFlapVisibility != null ) writer.writeProperty( frontFlapVisibility, "Front flap device visibility" ); if ( rearFlapVisibility != null ) writer.writeProperty( rearFlapVisibility, "Rear flap device visibility" ); writer.writeProperty( imageBackgroundImageOff, "Background image for an aid's 'off' state" ); writer.writeProperty( imageBackgroundImageLow, "Background image for an aid's 'low' state" ); writer.writeProperty( imageBackgroundImageMedium, "Background image for an aid's 'medium' state" ); writer.writeProperty( imageBackgroundImageHigh, "Background image for an aid's 'high' state" ); writer.writeProperty( imageSpeedLimiterOff, null ); writer.writeProperty( imageSpeedLimiterOn, null ); writer.writeProperty( imageIgnitionOff, null ); writer.writeProperty( imageIgnitionOn, null ); writer.writeProperty( imageIgnitionStarter, null ); writer.writeProperty( imageFrontFlapDisallowed, null ); writer.writeProperty( imageFrontFlapNotYetAllowed, null ); writer.writeProperty( imageFrontFlapAllowed, null ); writer.writeProperty( imageFrontFlapActivated, null ); writer.writeProperty( imageRearFlapDisallowed, null ); writer.writeProperty( imageRearFlapNotYetAllowed, null ); writer.writeProperty( imageRearFlapAllowed, null ); writer.writeProperty( imageRearFlapActivated, null ); } /** * {@inheritDoc} */ @Override public void loadProperty( PropertyLoader loader, LiveGameData gameData ) { super.loadProperty( loader, gameData ); initStateVisibilityProperties(); if ( loader.loadProperty( columns ) ); else if ( loader.loadProperty( gap ) ); else if ( loader.loadProperty( alignment ) ); else if ( ( speedLimiterVisibility != null ) && loader.loadProperty( speedLimiterVisibility ) ); else if ( loader.loadProperty( blinkSpeedLimiter ) ); else if ( ( ignitionVisibility != null ) && loader.loadProperty( ignitionVisibility ) ); else if ( ( frontFlapVisibility != null ) && loader.loadProperty( frontFlapVisibility ) ); else if ( ( rearFlapVisibility != null ) && loader.loadProperty( rearFlapVisibility ) ); else if ( loader.loadProperty( imageBackgroundImageOff ) ); else if ( loader.loadProperty( imageBackgroundImageLow ) ); else if ( loader.loadProperty( imageBackgroundImageMedium ) ); else if ( loader.loadProperty( imageBackgroundImageHigh ) ); else if ( loader.loadProperty( imageSpeedLimiterOff ) ); else if ( loader.loadProperty( imageSpeedLimiterOn ) ); else if ( loader.loadProperty( imageIgnitionOff ) ); else if ( loader.loadProperty( imageIgnitionOn ) ); else if ( loader.loadProperty( imageIgnitionStarter ) ); else if ( loader.loadProperty( imageFrontFlapDisallowed ) ); else if ( loader.loadProperty( imageFrontFlapNotYetAllowed ) ); else if ( loader.loadProperty( imageFrontFlapAllowed ) ); else if ( loader.loadProperty( imageFrontFlapActivated ) ); else if ( loader.loadProperty( imageRearFlapDisallowed ) ); else if ( loader.loadProperty( imageRearFlapNotYetAllowed ) ); else if ( loader.loadProperty( imageRearFlapAllowed ) ); else if ( loader.loadProperty( imageRearFlapActivated ) ); } @Override protected boolean hasText() { return ( false ); } /** * {@inheritDoc} */ @Override public void getProperties( PropertiesContainer propsCont, boolean forceAll ) { super.getProperties( propsCont, forceAll ); initStateVisibilityProperties(); propsCont.addProperty( imageBackgroundImageOff ); propsCont.addProperty( imageBackgroundImageLow ); propsCont.addProperty( imageBackgroundImageMedium ); propsCont.addProperty( imageBackgroundImageHigh ); propsCont.addGroup( "Device icons" ); propsCont.addProperty( imageSpeedLimiterOff ); propsCont.addProperty( imageSpeedLimiterOn ); propsCont.addProperty( imageIgnitionOff ); propsCont.addProperty( imageIgnitionOn ); propsCont.addProperty( imageIgnitionStarter ); propsCont.addProperty( imageFrontFlapDisallowed ); propsCont.addProperty( imageFrontFlapNotYetAllowed ); propsCont.addProperty( imageFrontFlapAllowed ); propsCont.addProperty( imageFrontFlapActivated ); propsCont.addProperty( imageRearFlapDisallowed ); propsCont.addProperty( imageRearFlapNotYetAllowed ); propsCont.addProperty( imageRearFlapAllowed ); propsCont.addProperty( imageRearFlapActivated ); propsCont.addGroup( "Misc" ); propsCont.addProperty( columns ); propsCont.addProperty( gap ); propsCont.addProperty( alignment ); propsCont.addGroup( "Device visibilities" ); propsCont.addProperty( speedLimiterVisibility ); propsCont.addProperty( blinkSpeedLimiter ); propsCont.addProperty( ignitionVisibility ); propsCont.addProperty( frontFlapVisibility ); propsCont.addProperty( rearFlapVisibility ); } @Override protected void initSubTextures( LiveGameData gameData, boolean isEditorMode, int widgetInnerWidth, int widgetInnerHeight, SubTextureCollector collector ) { int smallerSize = Math.min( widgetInnerWidth, widgetInnerHeight ); iconSize = (int)Math.floor( ( smallerSize - gap.getFloatValue() * ( columns.getFloatValue() - 1 ) ) / columns.getFloatValue() ); innerIconSize = iconSize * 3 / 4; innerIconOffset = ( iconSize - innerIconSize ) / 2; imageBackgroundImageOff.updateSize( iconSize, iconSize, isEditorMode ); imageBackgroundImageLow.updateSize( iconSize, iconSize, isEditorMode ); imageBackgroundImageMedium.updateSize( iconSize, iconSize, isEditorMode ); imageBackgroundImageHigh.updateSize( iconSize, iconSize, isEditorMode ); imageSpeedLimiterOff.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageSpeedLimiterOn.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageIgnitionOff.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageIgnitionOn.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageIgnitionStarter.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageFrontFlapDisallowed.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageFrontFlapNotYetAllowed.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageFrontFlapAllowed.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageFrontFlapActivated.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageRearFlapDisallowed.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageRearFlapNotYetAllowed.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageRearFlapAllowed.updateSize( innerIconSize, innerIconSize, isEditorMode ); imageRearFlapActivated.updateSize( innerIconSize, innerIconSize, isEditorMode ); } private void initStateVisibilityProperties() { if ( speedLimiterVisibility == null ) { { List<DeviceStateValue> values = new ArrayList<DeviceStateValue>(); values.add( DeviceStateValue.never ); values.add( DeviceStateValue.always ); DeviceStateValue inoff = new DeviceStateValue( 0, "0", "If not Off" ); DeviceStateValue inon = new DeviceStateValue( 1, "1", "If not On" ); values.add( inoff ); values.add( inon ); speedLimiterVisibility = new ListProperty<DeviceStateValue, List<DeviceStateValue>>( "speedLimiterVisibility", "speedLimiter", inoff, values ) { @Override protected void onValueChanged( DeviceStateValue oldValue, DeviceStateValue newValue ) { forceCompleteRedraw( false ); } }; } { List<DeviceStateValue> values = new ArrayList<DeviceStateValue>(); values.add( DeviceStateValue.never ); values.add( DeviceStateValue.always ); DeviceStateValue inoff = new DeviceStateValue( 0, "0", "If not Off" ); DeviceStateValue inon = new DeviceStateValue( 1, "1", "If not On" ); values.add( inoff ); values.add( inon ); ignitionVisibility = new ListProperty<DeviceStateValue, List<DeviceStateValue>>( "ignitionVisibility", "ignition", inon, values ) { @Override protected void onValueChanged( DeviceStateValue oldValue, DeviceStateValue newValue ) { forceCompleteRedraw( false ); } }; } { List<DeviceStateValue> values = new ArrayList<DeviceStateValue>(); values.add( DeviceStateValue.never ); values.add( DeviceStateValue.always ); DeviceStateValue indis = new DeviceStateValue( 0, "0", "If not Disallowed" ); values.add( indis ); frontFlapVisibility = new ListProperty<DeviceStateValue, List<DeviceStateValue>>( "frontFlapVisibility", "frontFlap", indis, values ) { @Override protected void onValueChanged( DeviceStateValue oldValue, DeviceStateValue newValue ) { forceCompleteRedraw( false ); } }; } { List<DeviceStateValue> values = new ArrayList<DeviceStateValue>(); values.add( DeviceStateValue.never ); values.add( DeviceStateValue.always ); DeviceStateValue indis = new DeviceStateValue( 0, "0", "If not Disallowed" ); values.add( indis ); rearFlapVisibility = new ListProperty<DeviceStateValue, List<DeviceStateValue>>( "rearFlapVisibility", "rearFlap", indis, values ) { @Override protected void onValueChanged( DeviceStateValue oldValue, DeviceStateValue newValue ) { forceCompleteRedraw( false ); } }; } } } /** * {@inheritDoc} */ @Override protected void initialize( LiveGameData gameData, boolean isEditorMode, DrawnStringFactory dsf, TextureImage2D texture, int width, int height ) { initStateVisibilityProperties(); } @Override public void prepareForMenuItem() { super.prepareForMenuItem(); initStateVisibilityProperties(); } @Override public void onCockpitEntered( LiveGameData gameData, boolean isEditorMode ) { super.onCockpitEntered( gameData, isEditorMode ); speedLimiterValue.reset( true ); speedLimiterDspValue.reset( true ); ignitionValue.reset( true ); frontFlapValue.reset( true ); rearFlapValue.reset( true ); nextBlinkTime = BLINK_INIT_VALUE; nextBlinkVis = true; } @Override public void onPitsEntered( LiveGameData gameData, boolean isEditorMode ) { nextBlinkTime = BLINK_INIT_VALUE; nextBlinkVis = true; } @Override public void onPitsExited( LiveGameData gameData, boolean isEditorMode ) { nextBlinkTime = BLINK_INIT_VALUE; nextBlinkVis = true; } private class RowCounter { int column = 0; int x = 0; int y = 0; void reset() { numRows = 0; column = 0; x = 0; y = 0; } void incColumn() { if ( numRows == 0 ) numRows = 1; column++; if ( column < columns.getIntValue() ) { x += iconSize; x += gap.getIntValue(); } else { column = 0; numRows++; x = 0; y += iconSize; y += gap.getIntValue(); } } } private final RowCounter rowCounter = new RowCounter(); private void countRows( TelemetryData telemData, VehicleScoringInfo vsi, boolean isEditorMode ) { numRows = -1; if ( alignment.getValue() == Alignment.CENTER ) { rowCounter.reset(); { DeviceStateValue dsv = speedLimiterVisibility.getValue(); if ( dsv != DeviceStateValue.never ) { if ( isEditorMode || ( dsv == DeviceStateValue.always ) || ( blinkSpeedLimiter.getBooleanValue() && ( ( vsi.isInPits() && !telemData.isSpeedLimiterOn() ) || ( !vsi.isInPits() && telemData.isSpeedLimiterOn() ) ) ) || ( ( dsv.state.intValue() == 1 ) != telemData.isSpeedLimiterOn() ) ) { rowCounter.incColumn(); } } } { DeviceStateValue dsv = ignitionVisibility.getValue(); if ( dsv != DeviceStateValue.never ) { int state2 = ( ( telemData.getIgnitionStatus() == IgnitionStatus.IGNITION_STARTER ) || ( telemData.getIgnitionStatus() == IgnitionStatus.IGNITION ) ) ? 1 : 0; if ( isEditorMode || ( dsv == DeviceStateValue.always ) || ( dsv.state.intValue() != state2 ) ) { rowCounter.incColumn(); } } } { DeviceStateValue dsv = frontFlapVisibility.getValue(); if ( dsv != DeviceStateValue.never ) { int state = getFlapState( telemData.getFrontFlapLegalStatus(), telemData.isFrontFlapActivated() ); int state2 = ( state == 0 ) ? 0 : 1; if ( isEditorMode || ( dsv == DeviceStateValue.always ) || ( dsv.state.intValue() != state2 ) ) { rowCounter.incColumn(); } } } { DeviceStateValue dsv = rearFlapVisibility.getValue(); if ( dsv != DeviceStateValue.never ) { int state = getFlapState( telemData.getRearFlapLegalStatus(), telemData.isRearFlapActivated() ); int state2 = ( state == 0 ) ? 0 : 1; if ( isEditorMode || ( dsv == DeviceStateValue.always ) || ( dsv.state.intValue() != state2 ) ) { rowCounter.incColumn(); } } } } } private TextureImage2D getIconBackground( int state ) { switch ( state ) { case 0: return ( imageBackgroundImageOff.getTexture() ); case 1: return ( imageBackgroundImageLow.getTexture() ); case 2: return ( imageBackgroundImageMedium.getTexture() ); case 3: default: return ( imageBackgroundImageHigh.getTexture() ); } } private int getFlapState( DeviceLegalStatus legalStatus, boolean activated ) { if ( activated ) return ( 3 ); if ( legalStatus != null ) { switch ( legalStatus ) { case CRITERIA_MET_BUT_NOT_YET_ALLOWED: return ( 1 ); case ALLOWED: return ( 2 ); } } return ( 0 ); } private void renderIcon( TextureImage2D background, TextureImage2D icon, TextureImage2D texture, int offsetX, int offsetY, int x, int y, int numRows ) { Alignment align = alignment.getValue(); int x2 = x; int y2 = y; if ( getInnerSize().getEffectiveWidth() > getInnerSize().getEffectiveHeight() ) { x2 = y; y2 = x; } int x3 = x2; int y3 = y2; if ( align == Alignment.CENTER ) { if ( getInnerSize().getEffectiveWidth() > getInnerSize().getEffectiveHeight() ) x3 = ( getInnerSize().getEffectiveWidth() - ( numRows * iconSize ) - ( numRows * gap.getIntValue() - gap.getIntValue() ) ) / 2 + x2; else y3 = ( getInnerSize().getEffectiveHeight() - ( numRows * iconSize ) - ( numRows * gap.getIntValue() - gap.getIntValue() ) ) / 2 + y2; } else if ( ( align == Alignment.TOP_RIGHT ) || ( align == Alignment.BOTTOM_RIGHT ) ) { x3 = getInnerSize().getEffectiveWidth() - x2 - iconSize; } int x4 = x3; int y4 = y3; if ( ( align == Alignment.BOTTOM_LEFT ) || ( align == Alignment.BOTTOM_RIGHT ) ) { y4 = getInnerSize().getEffectiveHeight() - y3 - iconSize; } //clearBackgroundRegion( texture, offsetX, offsetY, x4, y4, iconSize, iconSize, true, null ); if ( background != null ) texture.drawImage( background, offsetX + x4, offsetY + y4, false, null ); if ( icon != null ) texture.drawImage( icon, offsetX + x4 + innerIconOffset, offsetY + y4 + innerIconOffset, false, null ); } @Override protected void drawWidget( Clock clock, boolean needsCompleteRedraw, LiveGameData gameData, boolean isEditorMode, TextureImage2D texture, int offsetX, int offsetY, int width, int height ) { final TelemetryData telemData = gameData.getTelemetryData(); final boolean isInPitLane = gameData.getScoringInfo().getPlayersVehicleScoringInfo().isInPits(); Alignment align = alignment.getValue(); if ( ( align == Alignment.CENTER ) && ( ( numRows < 0 ) || isEditorMode ) ) countRows( telemData, gameData.getScoringInfo().getPlayersVehicleScoringInfo(), isEditorMode ); int numRows = this.numRows; rowCounter.reset(); boolean allCleared = false; boolean anythingRendered = false; int numSkipped = 0; while ( !anythingRendered && ( numSkipped < 4 ) ) { numSkipped = 0; { boolean skipped = true; DeviceStateValue dsv = speedLimiterVisibility.getValue(); if ( dsv != DeviceStateValue.never ) { if ( speedLimiterValue.update( telemData.isSpeedLimiterOn() ) ) { nextBlinkTime = BLINK_INIT_VALUE; nextBlinkVis = true; } boolean dsp = isEditorMode || telemData.isSpeedLimiterOn(); boolean blink = blinkSpeedLimiter.getBooleanValue() && ( ( isInPitLane && !telemData.isSpeedLimiterOn() ) || ( !isInPitLane && telemData.isSpeedLimiterOn() ) ); if ( blink && !isEditorMode ) { if ( nextBlinkTime == BLINK_INIT_VALUE ) { nextBlinkTime = gameData.getScoringInfo().getSessionNanos() + BLINK_DELAY; if ( speedLimiterDspValue.getValue() == speedLimiterDspValue.getResetValue() ) nextBlinkVis = false; else nextBlinkVis = speedLimiterDspValue.getValue(); } else if ( gameData.getScoringInfo().getSessionNanos() >= nextBlinkTime ) { nextBlinkTime = gameData.getScoringInfo().getSessionNanos() + BLINK_DELAY; nextBlinkVis = !nextBlinkVis; } dsp = !nextBlinkVis; } if ( ( speedLimiterValue.hasChanged( false ) | speedLimiterDspValue.update( dsp ) ) || allCleared || needsCompleteRedraw ) { if ( !allCleared ) { clearBackgroundRegion( texture, offsetX, offsetY, 0, 0, getInnerSize().getEffectiveWidth(), getInnerSize().getEffectiveHeight(), true, null ); allCleared = true; continue; } if ( isEditorMode || ( dsv == DeviceStateValue.always ) || blink || ( ( dsv.state.intValue() == 1 ) != telemData.isSpeedLimiterOn() ) ) { if ( dsp ) { TextureImage2D background = getIconBackground( telemData.isSpeedLimiterOn() ? 3 : 0 ); TextureImage2D icon = telemData.isSpeedLimiterOn() ? imageSpeedLimiterOn.getTexture() : imageSpeedLimiterOff.getTexture(); renderIcon( background, icon, texture, offsetX, offsetY, rowCounter.x, rowCounter.y, numRows ); } rowCounter.incColumn(); skipped = false; anythingRendered = true; } speedLimiterValue.setUnchanged(); speedLimiterDspValue.setUnchanged(); } } if ( skipped ) numSkipped++; } { boolean skipped = true; DeviceStateValue dsv = ignitionVisibility.getValue(); if ( dsv != DeviceStateValue.never ) { int state2 = ( ( telemData.getIgnitionStatus() == IgnitionStatus.IGNITION_STARTER ) || ( telemData.getIgnitionStatus() == IgnitionStatus.IGNITION ) ) ? 1 : 0; if ( ignitionValue.update( telemData.getIgnitionStatus() ) || allCleared || needsCompleteRedraw ) { if ( !allCleared ) { clearBackgroundRegion( texture, offsetX, offsetY, 0, 0, getInnerSize().getEffectiveWidth(), getInnerSize().getEffectiveHeight(), true, null ); allCleared = true; continue; } if ( ( isEditorMode || ( dsv == DeviceStateValue.always ) || ( dsv.state.intValue() != state2 ) ) && ( telemData.getIgnitionStatus() != null ) ) { TextureImage2D background; TextureImage2D icon; switch ( telemData.getIgnitionStatus() ) { case IGNITION_STARTER: background = imageBackgroundImageHigh.getTexture(); icon = imageIgnitionStarter.getTexture(); break; case IGNITION: background = imageBackgroundImageMedium.getTexture(); icon = imageIgnitionOn.getTexture(); break; case OFF: default: background = imageBackgroundImageOff.getTexture(); icon = imageIgnitionOff.getTexture(); break; } renderIcon( background, icon, texture, offsetX, offsetY, rowCounter.x, rowCounter.y, numRows ); rowCounter.incColumn(); skipped = false; anythingRendered = true; } ignitionValue.setUnchanged(); } } if ( skipped ) numSkipped++; } { boolean skipped = true; DeviceStateValue dsv = frontFlapVisibility.getValue(); if ( dsv != DeviceStateValue.never ) { int state = getFlapState( telemData.getFrontFlapLegalStatus(), telemData.isFrontFlapActivated() ); int state2 = ( state == 0 ) ? 0 : 1; if ( frontFlapValue.update( state ) || allCleared || needsCompleteRedraw ) { if ( !allCleared ) { clearBackgroundRegion( texture, offsetX, offsetY, 0, 0, getInnerSize().getEffectiveWidth(), getInnerSize().getEffectiveHeight(), true, null ); allCleared = true; continue; } if ( isEditorMode || ( dsv == DeviceStateValue.always ) || ( dsv.state.intValue() != state2 ) ) { TextureImage2D background = getIconBackground( state ); TextureImage2D icon; switch ( state ) { case 1: icon = imageFrontFlapNotYetAllowed.getTexture(); break; case 2: icon = imageFrontFlapAllowed.getTexture(); break; case 3: icon = imageFrontFlapActivated.getTexture(); break; case 0: default: icon = imageFrontFlapDisallowed.getTexture(); break; } renderIcon( background, icon, texture, offsetX, offsetY, rowCounter.x, rowCounter.y, numRows ); rowCounter.incColumn(); skipped = false; anythingRendered = true; } frontFlapValue.setUnchanged(); } } if ( skipped ) numSkipped++; } { boolean skipped = true; DeviceStateValue dsv = rearFlapVisibility.getValue(); if ( dsv != DeviceStateValue.never ) { int state = getFlapState( telemData.getRearFlapLegalStatus(), telemData.isRearFlapActivated() ); int state2 = ( state == 0 ) ? 0 : 1; if ( frontFlapValue.update( state ) || allCleared || needsCompleteRedraw ) { if ( !allCleared ) { clearBackgroundRegion( texture, offsetX, offsetY, 0, 0, getInnerSize().getEffectiveWidth(), getInnerSize().getEffectiveHeight(), true, null ); allCleared = true; continue; } if ( isEditorMode || ( dsv == DeviceStateValue.always ) || ( dsv.state.intValue() != state2 ) ) { TextureImage2D background = getIconBackground( state ); TextureImage2D icon; switch ( state ) { case 1: icon = imageRearFlapNotYetAllowed.getTexture(); break; case 2: icon = imageRearFlapAllowed.getTexture(); break; case 3: icon = imageRearFlapActivated.getTexture(); break; case 0: default: icon = imageRearFlapDisallowed.getTexture(); break; } renderIcon( background, icon, texture, offsetX, offsetY, rowCounter.x, rowCounter.y, numRows ); rowCounter.incColumn(); skipped = false; anythingRendered = true; } rearFlapValue.setUnchanged(); } } if ( skipped ) numSkipped++; } } } }