package mock.model; import shared.model.*; /** * Represents a Boat on the mock side of a race. * This adds mock specific functionality to a boat. */ public class MockBoat extends Boat { /** * This stores a boat's polars table. * Can be used to calculate VMG. */ private Polars polars; /** * This stores the milliseconds since the boat has changed its tack, to allow for only updating the tack every X milliseconds. */ private long timeSinceTackChange = 0; /** * This stores the boats current status of rounding a mark * 0: not started rounding * 1: passed only first check * 2: passed first and second check */ private Integer roundingStatus = 0; /** * Stores whether the boat is on autoVMG or not */ private boolean autoVMG = false; /** * Constructs a boat object with a given sourceID, name, country/team abbreviation, and polars table. * * @param sourceID The id of the boat * @param name The name of the Boat. * @param country The abbreviation or country code for the boat. * @param polars The polars table to use for this boat. */ public MockBoat(int sourceID, String name, String country, Polars polars) { super(sourceID, name, country); this.polars = polars; } /** * Constructs a mock boat object from a given boat and polars table. * * @param boat The boat to convert into a MockBoat. * @param polars The polars table to use for this boat. */ public MockBoat(Boat boat, Polars polars) { super(boat.getSourceID(), boat.getName(), boat.getCountry()); this.polars = polars; } /** * Calculate the bearing of the boat to its next marker. * @return The bearing to the next marker. */ public Bearing calculateBearingToNextMarker() { //Get the start and end points. GPSCoordinate currentPosition = this.getCurrentPosition(); GPSCoordinate nextMarkerPosition = this.getCurrentLeg().getEndCompoundMark().getAverageGPSCoordinate(); //Calculate bearing. Bearing bearing = GPSCoordinate.calculateBearing(currentPosition, nextMarkerPosition); return bearing; } /** * Calculates the distance between the boat and its target marker in nautical miles. * @return The distance (in nautical miles) between the boat and its target marker. */ public double calculateDistanceToNextMarker() { //Get start and end markers. GPSCoordinate startPosition = this.getCurrentPosition(); //When boats finish, their "current leg" doesn't have an end marker. if (this.getCurrentLeg().getEndCompoundMark() == null) { return 0d; } GPSCoordinate endMarker = this.getCurrentLeg().getEndCompoundMark().getAverageGPSCoordinate(); //Calculate distance. double distanceNauticalMiles = GPSCoordinate.calculateDistanceNauticalMiles(startPosition, endMarker); return distanceNauticalMiles; } /** * Returns the polars table for this boat. * @return The polars table for this boat. */ public Polars getPolars() { return polars; } /** * Sets the polars table for this boat. * @param polars The new polars table for this boat. */ public void setPolars(Polars polars) { this.polars = polars; } /** * Returns the time since the boat changed its tack, in milliseconds. * @return Time since the boat changed its tack, in milliseconds. */ public long getTimeSinceTackChange() { return timeSinceTackChange; } /** * Sets the time since the boat changed it's tack, in milliseconds. * @param timeSinceTackChange Time since the boat changed its tack, in milliseconds. */ public void setTimeSinceTackChange(long timeSinceTackChange) { this.timeSinceTackChange = timeSinceTackChange; } /** * Moves the boat meters forward in the direction that it is facing * @param meters The number of meters to move forward. * */ public void moveForwards(double meters) { //Updates the current position of the boat. GPSCoordinate newPosition = GPSCoordinate.calculateNewPosition(this.getCurrentPosition(), meters, Azimuth.fromBearing(this.getBearing())); this.setCurrentPosition(newPosition); } /** * Sets the boats speed and bearing to those in the given VMG. * @param newVMG The new VMG to use for the boat - contains speed and bearing. */ public void setVMG(VMG newVMG) { this.setBearing(newVMG.getBearing()); this.setCurrentSpeed(newVMG.getSpeed()); this.setTimeSinceTackChange(0); } /** * Calculates the number of nautical miles the boat will travel in a given time slice. * E.g., in 53 milliseconds a boat may travel 0.0002 nautical miles. * @param timeSlice The timeslice to use. * @return The distance travelled, in nautical miles, over the given timeslice. */ public double calculateNauticalMilesTravelled(long timeSlice) { //The proportion of one hour the current timeslice is. //This will be a low fractional number, so we need to go from long -> double. double hourProportion = ((double) timeSlice) / Constants.OneHourMilliseconds; //Calculates the distance travelled, in nautical miles, in the current timeslice. //distanceTravelledNM = speed (nm p hr) * time taken to update loop double distanceTravelledNM = this.getCurrentSpeed() * hourProportion; return distanceTravelledNM; } /** * Calculates the number of meters the boat will travel in a given time slice. * E.g., in 53 milliseconds a boat may travel 0.02 meters. * @param timeSlice The timeslice to use. * @return The distance travelled, in meters, over the given timeslice. */ public double calculateMetersTravelled(long timeSlice) { //Calculate the distance travelled, in nautical miles. double distanceTravelledNM = this.calculateNauticalMilesTravelled(timeSlice); //Convert to meters. double distanceTravelledMeters = distanceTravelledNM * Constants.NMToMetersConversion; return distanceTravelledMeters; } /** * Check if a mark is on the port side of the boat * @param mark mark to be passed * @return true if mark is on port side */ public boolean isPortSide(Mark mark){ //if this boat is lower than the mark check which way it is facing if(this.getCurrentPosition().getLongitude() < mark.getPosition().getLongitude()){ return this.getBearing().degrees() <= 180; }else{ return this.getBearing().degrees() > 180; } } /** * Check if a mark is on the starboard side of the boat * @param mark mark to be passed * @return true if mark is on starboard side */ public boolean isStarboardSide(Mark mark){ //if this boat is lower than the mark check which way it is facing if(this.getCurrentPosition().getLongitude() < mark.getPosition().getLongitude()){ return this.getBearing().degrees() >= 180; }else{ return this.getBearing().degrees() < 180; } } /** * Used to check if this boat is between a gate * @param gate the gate to be checked * @return true if the boat is between two marks that make up a gate */ public boolean isBetweenGate(CompoundMark gate){ if ((this.isPortSide(gate.getMark1()) && this.isStarboardSide(gate.getMark2())) || (this.isStarboardSide(gate.getMark2()) && this.isPortSide(gate.getMark1()))){ return true; }else{ return false; } } public Integer getRoundingStatus() { return Integer.valueOf(roundingStatus); } public void increaseRoundingStatus() { this.roundingStatus++; } public void resetRoundingStatus() { this.roundingStatus = 0; } public boolean isAutoVMG() { return autoVMG; } public void setAutoVMG(boolean autoVMG) { this.autoVMG = autoVMG; } public boolean getAutoVMG(){ return autoVMG; } }