/** * 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.etv2010.sessionstate; import java.awt.Color; import java.awt.FontMetrics; import java.awt.geom.Rectangle2D; import java.io.IOException; import net.ctdp.rfdynhud.gamedata.GamePhase; import net.ctdp.rfdynhud.gamedata.LiveGameData; import net.ctdp.rfdynhud.gamedata.ScoringInfo; import net.ctdp.rfdynhud.gamedata.SessionLimit; import net.ctdp.rfdynhud.gamedata.SessionType; import net.ctdp.rfdynhud.gamedata.VehicleScoringInfo; import net.ctdp.rfdynhud.gamedata.YellowFlagState; import net.ctdp.rfdynhud.properties.EnumProperty; import net.ctdp.rfdynhud.properties.PropertyLoader; import net.ctdp.rfdynhud.properties.PropertiesContainer; import net.ctdp.rfdynhud.render.DrawnString; import net.ctdp.rfdynhud.render.DrawnString.Alignment; import net.ctdp.rfdynhud.render.DrawnStringFactory; import net.ctdp.rfdynhud.render.Texture2DCanvas; import net.ctdp.rfdynhud.render.TextureImage2D; import net.ctdp.rfdynhud.util.SubTextureCollector; import net.ctdp.rfdynhud.util.TimingUtil; import net.ctdp.rfdynhud.util.PropertyWriter; import net.ctdp.rfdynhud.valuemanagers.Clock; import net.ctdp.rfdynhud.values.BoolValue; import net.ctdp.rfdynhud.values.EnumValue; import net.ctdp.rfdynhud.values.FloatValue; import net.ctdp.rfdynhud.values.IntValue; import net.ctdp.rfdynhud.widgets.base.widget.Widget; import net.ctdp.rfdynhud.widgets.etv2010._base.ETVWidgetBase; import net.ctdp.rfdynhud.widgets.etv2010._util.ETVUtils; import net.ctdp.rfdynhud.widgets.etv2010._util.ETVWidgetSet; import net.ctdp.rfdynhud.widgets.etv2010._util.ETVImages.BGType; /** * The {@link ETVSessionStateWidget} displays the current lap. * * @author Marvin Froehlich (CTDP) */ public class ETVSessionStateWidget extends ETVWidgetBase { private final EnumProperty<SessionLimit> sessionLimitPreference = new EnumProperty<SessionLimit>( "sessionLimitPreference", "sessionLimitPref", SessionLimit.LAPS ); private SessionLimit sessionLimit = SessionLimit.LAPS; private DrawnString captionString = null; private DrawnString stateString = null; private String caption = getCaption( SessionType.RACE1, SessionLimit.LAPS ); private final EnumValue<GamePhase> gamePhase = new EnumValue<GamePhase>(); private final EnumValue<YellowFlagState> yellowFlagState = new EnumValue<YellowFlagState>( YellowFlagState.NONE ); private final BoolValue sectorYellowFlag = new BoolValue(); private final IntValue lap = new IntValue(); private final FloatValue sessionTime = new FloatValue( -1f, 0.1f ); private BGType bgType = BGType.NEUTRAL; private Color dataBgColor = Color.MAGENTA; private Color dataFontColor = Color.GREEN; private static final Alignment[] colAligns = new Alignment[] { Alignment.RIGHT, Alignment.CENTER, Alignment.RIGHT }; private final int[] colWidths = new int[ 3 ]; private static final int colPadding = 10; public ETVSessionStateWidget() { super( ETVWidgetSet.INSTANCE, ETVWidgetSet.WIDGET_PACKAGE, 12.0f, 2.54f ); } /** * {@inheritDoc} */ @Override public void saveProperties( PropertyWriter writer ) throws IOException { super.saveProperties( writer ); writer.writeProperty( sessionLimitPreference, "If a session is limited by both laps and time, this limit will be displayed." ); } /** * {@inheritDoc} */ @Override public void loadProperty( PropertyLoader loader ) { super.loadProperty( loader ); if ( loader.loadProperty( sessionLimitPreference ) ); } /** * {@inheritDoc} */ @Override protected void getItemGapProperty( PropertiesContainer propsCont, boolean forceAll ) { // No super call. We don't need the item gap here! } /** * {@inheritDoc} */ @Override public void getProperties( PropertiesContainer propsCont, boolean forceAll ) { super.getProperties( propsCont, forceAll ); propsCont.addGroup( "Misc" ); propsCont.addProperty( sessionLimitPreference ); } /** * {@inheritDoc} */ @Override public int getNeededData() { return ( Widget.NEEDED_DATA_SCORING ); } private static final String getCaption( SessionType sessionType, SessionLimit sessionLimit ) { switch ( sessionType ) { case TEST_DAY: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_TEST_DAY_time ); return ( Loc.caption_TEST_DAY_laps ); case PRACTICE1: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_PRACTICE1_time ); return ( Loc.caption_PRACTICE1_laps ); case PRACTICE2: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_PRACTICE2_time ); return ( Loc.caption_PRACTICE2_laps ); case PRACTICE3: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_PRACTICE3_time ); return ( Loc.caption_PRACTICE3_laps ); case PRACTICE4: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_PRACTICE4_time ); return ( Loc.caption_PRACTICE4_laps ); case QUALIFYING1: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_QUALIFYING1_time ); return ( Loc.caption_QUALIFYING1_laps ); case QUALIFYING2: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_QUALIFYING2_time ); return ( Loc.caption_QUALIFYING2_laps ); case QUALIFYING3: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_QUALIFYING3_time ); return ( Loc.caption_QUALIFYING3_laps ); case QUALIFYING4: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_QUALIFYING4_time ); return ( Loc.caption_QUALIFYING4_laps ); case WARMUP: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_WARMUP_time ); return ( Loc.caption_WARMUP_laps ); case RACE1: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_RACE1_time ); return ( Loc.caption_RACE1_laps ); case RACE2: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_RACE2_time ); return ( Loc.caption_RACE2_laps ); case RACE3: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_RACE3_time ); return ( Loc.caption_RACE3_laps ); case RACE4: if ( sessionLimit == SessionLimit.TIME ) return ( Loc.caption_RACE4_time ); return ( Loc.caption_RACE4_laps ); } // Unreachable code! return ( "N/A" ); } private boolean updateSessionLimit( LiveGameData gameData ) { final ScoringInfo scoringInfo = gameData.getScoringInfo(); SessionLimit oldSessionLimit = sessionLimit; String oldCaption = caption; sessionLimit = scoringInfo.getViewedVehicleScoringInfo().getSessionLimit( sessionLimitPreference.getEnumValue() ); caption = getCaption( scoringInfo.getSessionType(), sessionLimit ); if ( ( sessionLimit != oldSessionLimit ) || !caption.equals( oldCaption ) ) { forceReinitialization(); forceCompleteRedraw( false ); return ( true ); } return ( false ); } @Override public void onSessionStarted( SessionType sessionType, LiveGameData gameData, boolean isEditorMode ) { super.onSessionStarted( sessionType, gameData, isEditorMode ); yellowFlagState.reset(); sectorYellowFlag.reset(); lap.reset(); sessionTime.reset(); gamePhase.reset(); } /** * {@inheritDoc} */ @Override protected void initSubTextures( LiveGameData gameData, boolean isEditorMode, int widgetInnerWidth, int widgetInnerHeight, SubTextureCollector collector ) { } /** * {@inheritDoc} */ @Override protected boolean checkForChanges( LiveGameData gameData, boolean isEditorMode, TextureImage2D texture, int width, int height ) { final ScoringInfo scoringInfo = gameData.getScoringInfo(); gamePhase.update( scoringInfo.getGamePhase() ); yellowFlagState.update( scoringInfo.getYellowFlagState() ); sectorYellowFlag.update( scoringInfo.getSectorYellowFlag( scoringInfo.getViewedVehicleScoringInfo().getSector() ) ); boolean changed = false; if ( gamePhase.hasChanged() ) changed = true; if ( yellowFlagState.hasChanged() ) changed = true; if ( sectorYellowFlag.hasChanged() ) changed = true; bgType = BGType.NEUTRAL; dataBgColor = dataBackgroundColor.getColor(); dataFontColor = getFontColor(); if ( ( gamePhase.getValue() == GamePhase.FORMATION_LAP ) || ( gamePhase.getValue() == GamePhase.FULL_COURSE_YELLOW ) || sectorYellowFlag.getValue() ) { bgType = BGType.LABEL_YELLOW; dataBgColor = Color.YELLOW; dataFontColor = Color.BLACK; } /* else if ( gamePhase.getValue() == GamePhase.GREEN_FLAG ) { dataBgColor = Color.GREEN; dataFontColor = Color.WHITE; } */ else if ( gamePhase.getValue() == GamePhase.SESSION_STOPPED ) { bgType = BGType.LABEL_RED; dataBgColor = Color.RED; dataFontColor = Color.WHITE; } if ( updateSessionLimit( gameData ) ) changed = true; return ( changed ); } /** * {@inheritDoc} */ @Override protected void initialize( LiveGameData gameData, boolean isEditorMode, DrawnStringFactory dsf, TextureImage2D texture, int width, int height ) { Texture2DCanvas texCanvas = texture.getTextureCanvas(); texCanvas.setFont( getFont() ); FontMetrics metrics = texCanvas.getFontMetrics(); Rectangle2D capBounds = metrics.getStringBounds( caption, texCanvas ); boolean useImages = this.useImages.getBooleanValue(); int dataAreaCenter = useImages ? getImages().getLabeledDataDataCenter( width, height, capBounds ) : ETVUtils.getLabeledDataDataCenter( width, height, capBounds ); int vMiddle = ETVUtils.getLabeledDataVMiddle( height, capBounds ); int captionLeft = useImages ? getImages().getLabeledDataCaptionLeft( height ) : ETVUtils.getTriangleWidth( height ); captionString = dsf.newDrawnString( "captionString", captionLeft, vMiddle, Alignment.LEFT, false, getFont(), isFontAntiAliased(), captionColor.getColor() ); stateString = dsf.newDrawnString( "stateString", dataAreaCenter, vMiddle, Alignment.CENTER, false, getFont(), isFontAntiAliased(), getFontColor() ); if ( sessionLimit == SessionLimit.LAPS ) stateString.getMinColWidths( new String[] { "00", "/", "00" }, colAligns, colPadding, colWidths ); forceCompleteRedraw( false ); } @Override protected void drawBackground( LiveGameData gameData, boolean isEditorMode, TextureImage2D texture, int offsetX, int offsetY, int width, int height, boolean isRoot ) { super.drawBackground( gameData, isEditorMode, texture, offsetX, offsetY, width, height, isRoot ); if ( useImages.getBooleanValue() ) ETVUtils.drawLabeledDataBackgroundI( offsetX, offsetY, width, height, caption, getFontProperty(), getImages(), bgType, texture, false ); else ETVUtils.drawLabeledDataBackground( offsetX, offsetY, width, height, caption, getFontProperty(), captionBackgroundColor.getColor(), dataBgColor, texture, false ); } @Override public void drawWidget( Clock clock, boolean needsCompleteRedraw, LiveGameData gameData, boolean isEditorMode, TextureImage2D texture, int offsetX, int offsetY, int width, int height ) { final ScoringInfo scoringInfo = gameData.getScoringInfo(); VehicleScoringInfo vsi = scoringInfo.getSessionType().isRace() ? scoringInfo.getLeadersVehicleScoringInfo() : scoringInfo.getViewedVehicleScoringInfo(); if ( needsCompleteRedraw ) { captionString.draw( offsetX, offsetY, caption, texture ); } if ( sessionLimit == SessionLimit.TIME ) { sessionTime.update( gameData.getScoringInfo().getSessionTime() ); float endTime = gameData.getScoringInfo().getEndTime(); if ( needsCompleteRedraw || ( clock.c() && ( sessionTime.hasChanged( false ) || gamePhase.hasChanged( false ) ) ) ) { sessionTime.setUnchanged(); gamePhase.setUnchanged(); if ( gamePhase.getValue() == GamePhase.SESSION_OVER ) stateString.draw( offsetX, offsetY, "00:00:00", dataFontColor, texture ); else if ( scoringInfo.getSessionType().isRace() && ( ( gamePhase.getValue() == GamePhase.FORMATION_LAP ) || ( endTime < 0f ) || ( endTime > 3000000f ) ) ) stateString.draw( offsetX, offsetY, "--:--:--", dataFontColor, texture ); else if ( scoringInfo.getSessionType().isTestDay() || ( endTime < 0f ) || ( endTime > 3000000f ) ) stateString.draw( offsetX, offsetY, TimingUtil.getTimeAsString( sessionTime.getValue(), true, false ), dataFontColor, texture ); else stateString.draw( offsetX, offsetY, TimingUtil.getTimeAsString( endTime - sessionTime.getValue(), true, false ), dataFontColor, texture ); } } else { if ( scoringInfo.getSessionType().isRace() && ( gamePhase.getValue() == GamePhase.FORMATION_LAP ) ) lap.update( 0 ); else if ( gameData.getProfileInfo().getShowCurrentLap() ) lap.update( vsi.getCurrentLap() ); else lap.update( vsi.getLapsCompleted() ); if ( needsCompleteRedraw || ( clock.c() && lap.hasChanged() ) ) { int maxLaps = scoringInfo.getMaxLaps(); String maxLapsStr = ( maxLaps < 10000 ) ? String.valueOf( maxLaps ) : "--"; stateString.drawColumns( offsetX, offsetY, new String[] { lap.getValueAsString(), "/", maxLapsStr }, colAligns, colPadding, colWidths, dataFontColor, texture ); } } } }