// SatelliteRushTest // By Thomas Padron-McCarthy (padrone@lysator.liu.se) // Latest change to this file: August 5, 2008 // No copyright. No warranty. No nothing. Share and enjoy! import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.lcdui.game.*; import javax.microedition.location.*; import java.util.Timer; import java.util.TimerTask; import java.util.Random; import java.util.Date; import java.util.Vector; import java.util.Enumeration; import henson.midp.Float11; // Some user-configurable game preferences class Preferences { public final double PADDLE_WIDTH_DEFAULT = 15.0; public final double BASE_SPEED_DEFAULT = 2.0; public final boolean SHOW_PATH_DEFAULT = true; public final boolean SATELLITE_MODE_DEFAULT = true; private double paddle_width_percent; // As a percentage of the screen width, 1..100 private double paddle_width_pixels; // In pixels, set from the paddle_width_percent private double base_speed_percent; // As a percentage of the screen diagonal per second private double base_speed_pixels; // In pixels per second, set from the base_speed_percent private boolean show_path; private double screen_width; private double screen_diagonal; boolean satellite_mode; public Preferences(int screen_width, int screen_height) { if (screen_width <= 0 || screen_height <= 0) throw new IllegalArgumentException("Impossible screen size"); this.screen_width = screen_width; this.screen_diagonal = Math.sqrt(screen_width * screen_width + screen_height * screen_height); resetDefaults(); } public void setPaddleWidth(double paddle_width_percent) { this.paddle_width_percent = paddle_width_percent; this.paddle_width_pixels = paddle_width_percent / 100.0 * screen_width; } public void setBaseSpeed(double base_speed_percent) { this.base_speed_percent = base_speed_percent; this.base_speed_pixels = base_speed_percent / 100.0 * screen_diagonal; } public double getPaddleWidthPercent() { return paddle_width_percent; } public double getPaddleWidthPixels() { return paddle_width_pixels; } public double getBaseSpeedPercent() { return base_speed_percent; } public double getBaseSpeedPixels() { return base_speed_pixels; } public void setShowPath(boolean show_path) { this.show_path = show_path; } public boolean getShowPath() { return show_path; } public void setSatelliteMode(boolean satellite_mode) { this.satellite_mode = satellite_mode; } public boolean getSatelliteMode() { return satellite_mode; } public void resetDefaults() { setPaddleWidth(PADDLE_WIDTH_DEFAULT); setBaseSpeed(BASE_SPEED_DEFAULT); setShowPath(SHOW_PATH_DEFAULT); setSatelliteMode(SATELLITE_MODE_DEFAULT); } } // Preferences // A Form that shows, and lets the user edit, some configuration options class PreferencesForm extends Form implements CommandListener { private Preferences preferences; private Display display; private Displayable window_under; private Coordinates current_coordinates; private Coordinates left_corner; private Coordinates right_corner; private double baseline; private double baseline_position; private LocationProvider location_provider; private Location current_location; private Command play_command = new Command("Play", Command.OK, 1); private Command save_command = new Command("Save", Command.SCREEN, 2); private Command start_command = new Command("Start listening", Command.SCREEN, 3); private Command left_command = new Command("Left corner", Command.SCREEN, 4); private Command right_command = new Command("Right corner", Command.SCREEN, 5); private Command defaults_command = new Command("Restore defaults", Command.SCREEN, 6); private Command stop_command = new Command("Stop listening", Command.STOP, 7); private Command edit_criteria_command = new Command("Edit Criteria", Command.SCREEN, 8); private TextField paddle_item; private TextField speed_item; private ChoiceGroup path_item; private ChoiceGroup mode_item; private StringItem distance_item; private StringItem position_item; private StringItem current_coordinates_item; private StringItem left_item; private StringItem right_item; private boolean is_listening = false; private boolean waiting_for_left = false; private boolean waiting_for_right = false; public PreferencesForm(Preferences preferences, Display display, Displayable window_under) { super("Preferences"); this.preferences = preferences; this.display = display; this.window_under = window_under; paddle_item = new TextField("Paddle size (in percent of screen width)", "", 10, TextField.DECIMAL); paddle_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(paddle_item); speed_item = new TextField("Base speed (in percent of screen diagonal)", "", 10, TextField.DECIMAL); speed_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(speed_item); path_item = new ChoiceGroup("Show ball path:", ChoiceGroup.MULTIPLE); path_item.append("Yes, show", null); path_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(path_item); mode_item = new ChoiceGroup("Mode:", ChoiceGroup.EXCLUSIVE); mode_item.append("Satellite mode", null); mode_item.append("Boring mode", null); mode_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(mode_item); distance_item = new StringItem("Baseline length: ", ""); distance_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(distance_item); position_item = new StringItem("Baseline position: ", ""); position_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(position_item); current_coordinates_item = new StringItem("Current: ", ""); current_coordinates_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(current_coordinates_item); left_item = new StringItem("Left corner: ", ""); left_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(left_item); right_item = new StringItem("Right corner: ", ""); right_item.setLayout(Item.LAYOUT_LEFT | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_NEWLINE_AFTER); append(right_item); load_values(); update_locations(); addCommand(play_command); addCommand(save_command); addCommand(defaults_command); addCommand(left_command); addCommand(right_command); addCommand(start_command); addCommand(stop_command); addCommand(edit_criteria_command); setCommandListener(this); } private String coordinates2string(Coordinates coordinates) { double lat = coordinates.getLatitude(); double lon = coordinates.getLongitude(); String lat_string = Coordinates.convert(lat, Coordinates.DD_MM_SS); String lon_string = Coordinates.convert(lon, Coordinates.DD_MM_SS); String value = "Lat. " + lat_string + ", long. " + lon_string; return value; } // Do this right! This is just an approximation, since I forget how to do it. public double OLD_baseline_position() { double distance2left = left_corner.distance(current_coordinates); double distance2right = right_corner.distance(current_coordinates); if (distance2right > baseline) return 0.0; else if (distance2left > baseline) return 1.0; else { return distance2left / baseline; } } public double get_baseline_position() { double distance2left = left_corner.distance(current_coordinates); if (distance2left == 0) { // System.out.println("get_baseline_position() = 0"); return 0; } double distance2right = right_corner.distance(current_coordinates); if (distance2right == 0) { // System.out.println("get_baseline_position() = 1"); return 1; } double cosA = (distance2left * distance2left + baseline * baseline - distance2right * distance2right) / (2 * distance2left * baseline); double distance2left_along_baseline = distance2left * cosA; if (distance2left_along_baseline < 0) { // System.out.println("get_baseline_position() = 0"); return 0; } else if (distance2left_along_baseline > baseline) { // System.out.println("get_baseline_position() = 1"); return 1; } else { // System.out.println("get_baseline_position() = " + distance2left_along_baseline / baseline); return distance2left_along_baseline / baseline; } } static private double round2(double x) { return ((int)(x * 100)) / 100.0; } private void update_locations() { if (left_corner == null || right_corner == null) distance_item.setText("n/a"); else distance_item.setText("" + round2(baseline) + " m"); if (current_coordinates == null || left_corner == null || right_corner == null) position_item.setText("n/a"); else { baseline_position = get_baseline_position(); position_item.setText("" + round2(baseline_position * 100) + " %"); } if (current_coordinates == null) current_coordinates_item.setText("null"); else current_coordinates_item.setText(coordinates2string(current_coordinates)); if (waiting_for_left) ; else if (left_corner == null) left_item.setText("null"); else left_item.setText(coordinates2string(left_corner)); if (waiting_for_right) ; else if (right_corner == null) right_item.setText("null"); else right_item.setText(coordinates2string(right_corner)); } // update_locations public void commandAction(Command c, Displayable s) { if (c == play_command) { if (preferences.getSatelliteMode() && (left_corner == null || right_corner == null)) MyAlert.show("Corner(s) missing", "In satellite mode, you must first set the left and the right corners, using GPS.", display); else if (preferences.getSatelliteMode() && !is_listening) MyAlert.show("Not listening", "In satellite mode, you must first start listening to GPS location updates.", display); else if (preferences.getSatelliteMode() && baseline < 30) MyAlert.show("Short baseline", "Your baseline is just " + round2(baseline) + " meters long. " + "I suggest a baseline of 100 meters or longer.", display); else display.setCurrent(window_under); } else if (c == save_command) { save_values(); } else if (c == defaults_command) { load_defaults(); } else if (c == left_command) { left(); } else if (c == right_command) { right(); } else if (c == start_command) { start_listening(); } else if (c == stop_command) { stop_listening(); } else if (c == edit_criteria_command) { edit_criteria(); } else { MyAlert.show("Internal error", "This can't happen.", display); } } private void save_values() { try { preferences.setPaddleWidth(Double.parseDouble(paddle_item.getString())); preferences.setBaseSpeed(Double.parseDouble(speed_item.getString())); boolean[] flag_array = new boolean[1]; path_item.getSelectedFlags(flag_array); preferences.setShowPath(flag_array[0]); flag_array = new boolean[2]; mode_item.getSelectedFlags(flag_array); preferences.setSatelliteMode(flag_array[0]); } catch (NumberFormatException e) { MyAlert.show("NumberFormatException", "Not the right kind of number. This can't happen.", display); } catch (Throwable e) { MyAlert.show("Throwable", "Some other error. Try again.", display); } } private void load_values() { paddle_item.setString("" + preferences.getPaddleWidthPercent()); speed_item.setString("" + preferences.getBaseSpeedPercent()); path_item.setSelectedIndex(0, preferences.getShowPath()); if (preferences.getSatelliteMode()) mode_item.setSelectedIndex(0, true); else mode_item.setSelectedIndex(1, true); } private void load_defaults() { preferences.resetDefaults(); load_values(); } private void left() { if (!is_listening) { MyAlert.show("Not listening", "Before you can set a corner, you must start listening to GPS location updates.", display); return; } Runnable r = new Runnable() { public void run() { try { if (location_provider == null) location_provider = LocationProvider.getInstance(criteria); QualifiedCoordinates qc; double accuracy; do { current_location = location_provider.getLocation(-1); qc = current_location.getQualifiedCoordinates(); current_coordinates = qc; accuracy = qc.getHorizontalAccuracy(); if (accuracy > 10) { left_item.setText("accuracy = " + accuracy + ", trying again..."); } } while (accuracy > 10); left_corner = current_coordinates; if (right_corner != null) baseline = left_corner.distance(right_corner); } catch(LocationException e) { MyAlert.show("LocationException", "There was a problem when trying to get a LocationProvider or a Location: " + e.toString(), display); } catch(InterruptedException e) { MyAlert.show("InterruptedException", "Reached timeout when trying to get a LocationProvider or a Location: " + e.toString(), display); } catch(Throwable e) { MyAlert.show("Throwable", "Some other error when trying to get a LocationProvider or a Location: " + e.toString(), display); } finally { update_locations(); } } // run }; // Runnable left_item.setText("waiting..."); if (is_listening) { waiting_for_left = true; } else { Thread t = new Thread(r); t.start(); } } // left private void right() { if (!is_listening) { MyAlert.show("Not listening", "Before you can set a corner, you must start listening to GPS location updates.", display); return; } Runnable r = new Runnable() { public void run() { try { if (location_provider == null) location_provider = LocationProvider.getInstance(criteria); QualifiedCoordinates qc; double accuracy; do { current_location = location_provider.getLocation(-1); qc = current_location.getQualifiedCoordinates(); current_coordinates = qc; accuracy = qc.getHorizontalAccuracy(); if (accuracy > 10) { right_item.setText("accuracy = " + accuracy + ", trying again..."); } } while (accuracy > 10); right_corner = current_coordinates; if (left_corner != null) baseline = left_corner.distance(right_corner); } catch(LocationException e) { MyAlert.show("LocationException", "There was a problem when trying to get a LocationProvider or a Location: " + e.toString(), display); } catch(InterruptedException e) { MyAlert.show("InterruptedException", "Reached timeout when trying to get a LocationProvider or a Location: " + e.toString(), display); } catch(Throwable e) { MyAlert.show("Throwable", "Some other error when trying to get a LocationProvider or a Location: " + e.toString(), display); } finally { update_locations(); } } // run }; // Runnable right_item.setText("waiting..."); if (is_listening) { waiting_for_right = true; } else { Thread t = new Thread(r); t.start(); } } // right private void get_position() { Runnable r = new Runnable() { public void run() { try { if (location_provider == null) location_provider = LocationProvider.getInstance(criteria); current_location = location_provider.getLocation(-1); if (current_location.isValid()) { QualifiedCoordinates qc = current_location.getQualifiedCoordinates(); current_coordinates = qc; } } catch(LocationException e) { MyAlert.show("LocationException", "There was a problem when trying to get a LocationProvider or a Location: " + e.toString(), display); } catch(InterruptedException e) { MyAlert.show("InterruptedException", "Reached timeout when trying to get a LocationProvider or a Location: " + e.toString(), display); } catch(Throwable e) { MyAlert.show("Throwable", "Some other error when trying to get a LocationProvider or a Location: " + e.toString(), display); } finally { update_locations(); } } // run }; // Runnable current_coordinates_item.setText("waiting..."); if (!is_listening) { Thread t = new Thread(r); t.start(); } } // get_position class MyLocationListener implements LocationListener { public MyLocationListener() { } public void locationUpdated(LocationProvider provider, Location location) { System.out.println("PRINTLN: " + "*** New location:"); if (location == null || !location.isValid()) { System.out.println("PRINTLN: " + "invalid location (null or not valid)"); return; } current_location = location; current_coordinates = current_location.getQualifiedCoordinates(); if (waiting_for_left) { QualifiedCoordinates qc = current_location.getQualifiedCoordinates(); double accuracy = qc.getHorizontalAccuracy(); if (accuracy > 10) { left_item.setText("accuracy = " + accuracy + ", trying again..."); } else { waiting_for_left = false; left_corner = current_coordinates; if (right_corner != null) baseline = left_corner.distance(right_corner); } } if (waiting_for_right) { QualifiedCoordinates qc = current_location.getQualifiedCoordinates(); double accuracy = qc.getHorizontalAccuracy(); if (accuracy > 10) { right_item.setText("accuracy = " + accuracy + ", trying again..."); } else { waiting_for_right = false; right_corner = current_coordinates; if (left_corner != null) baseline = left_corner.distance(right_corner); } } update_locations(); double lat = current_coordinates.getLatitude(); double lon = current_coordinates.getLongitude(); System.out.println("PRINTLN: " + " Latitude = " + lat); System.out.println("PRINTLN: " + " Longitude = " + lon); System.out.println("PRINTLN: " + "---"); } public void providerStateChanged(LocationProvider provider, int newState) { System.out.println("PRINTLN: " + "*** New provider state:"); System.out.println("PRINTLN: " + "" + newState); } } private void start_listening() { if (is_listening) { MyAlert.show("Already listening", "This program already is listening to GPS location updates.", display); return; } Runnable r = new Runnable() { public void run() { try { if (location_provider == null) location_provider = LocationProvider.getInstance(criteria); LocationListener listener = new MyLocationListener(); location_provider.setLocationListener(listener, -1, -1, -1); is_listening = true; } catch(LocationException e) { MyAlert.show("LocationException", "There was a problem when trying to get a LocationProvider or starting to listen: " + e.toString(), display); } catch(Throwable e) { MyAlert.show("Throwable", "Some other error when trying to get a LocationProvider or starting to listen: " + e.toString(), display); } } // run }; // Runnable Thread t = new Thread(r); t.start(); } private void stop_listening() { if (!is_listening) { MyAlert.show("Not listening", "There is no need to stop listening when you haven't started!", display); return; } Runnable r = new Runnable() { public void run() { try { if (location_provider != null) location_provider.setLocationListener(null, -1, -1, -1); is_listening = false; } catch(Throwable e) { MyAlert.show("Throwable", "Some other error when trying to get a LocationProvider or starting to listen: " + e.toString(), display); } } // run }; // Runnable Thread t = new Thread(r); t.start(); } // stop_listening private Criteria criteria = new Criteria(); private CriteriaForm criteria_form = null; private void edit_criteria() { if (criteria_form == null) criteria_form = new CriteriaForm(criteria, display, this); display.setCurrent(criteria_form); } } // PreferencesForm // An Alert, with an image and a static show method class MyAlert extends Alert { private static Image image = null; public MyAlert(String title, String text) { super(title, text, null, null); if (image == null) { try { image = Image.createImage("/warning.png"); } catch (java.io.IOException e) { // Ok, the image didn't work. Ignore it. } } setImage(image); setTimeout(Alert.FOREVER); } public static void show(String title, String text, Display display) { MyAlert alert = new MyAlert(title, text); display.setCurrent(alert); } } // Used by YesOrNoDialogue to report the user's answer to the caller interface YesOrNoCallback { public void gotAnswer(boolean answer); } // A dialogue that waits for a "yes" or "no" answer from the user class YesOrNoDialogue extends Form implements CommandListener { private Command yes_command = new Command("Yes", Command.OK, 0); private Command no_command = new Command("No", Command.CANCEL, 0); YesOrNoCallback callback; private Display display; public YesOrNoDialogue(String question, YesOrNoCallback callback, Display display) { super("Question"); this.callback = callback; this.display = display; addCommand(yes_command); addCommand(no_command); setCommandListener(this); // StringItem question_item = new StringItem("Question:", question); StringItem question_item = new StringItem(null, question); question_item.setLayout(Item.LAYOUT_CENTER | Item.LAYOUT_VCENTER); append(question_item); } public void commandAction(Command c, Displayable s) { if (c == no_command) { callback.gotAnswer(false); } else if (c == yes_command) { callback.gotAnswer(true); } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } } // YesOrNoDialogue // A Form that shows a help text for this program class HelpForm extends Form implements CommandListener { private Command back_command = new Command("Back", Command.BACK, 0); private Display display; private Displayable window_under; public HelpForm(Display display, Displayable window_under) { super("Instructions"); this.display = display; this.window_under = window_under; addCommand(back_command); setCommandListener(this); StringItem help_item = new StringItem(null, "Start by selecting satellite mode or boring mode. " + "If using satellite mode, then go to the two corners of the baseline " + "and give commands to set the left corner and the right corner. " + "Then save the configuration, and start playing. Run fast!"); append(help_item); } public void commandAction(Command c, Displayable s) { if (c == back_command) { display.setCurrent(window_under); } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } } // HelpForm // A Form that shows some information about this program class AboutForm extends Form implements CommandListener { private Command back_command = new Command("Back", Command.BACK, 0); private Display display; private Displayable window_under; public AboutForm(Display display, Displayable window_under) { super("About SatelliteRushTest"); this.display = display; this.window_under = window_under; addCommand(back_command); setCommandListener(this); StringItem info_item = new StringItem(null, "This is SatelliteRushTest, a more physical version of an old game idea. " + "SatelliteRushTest is a technical test version. " + " The real game will be called SatelliteRush."); append(info_item); StringItem version_item = new StringItem("Version:", "0.1 (August 5, 2008)"); append(version_item); StringItem author_item = new StringItem("Author:", "Thomas Padron-McCarthy"); append(author_item); StringItem mail_item = new StringItem("E-mail:", "padrone@lysator.liu.se"); append(mail_item); } public void commandAction(Command c, Displayable s) { if (c == back_command) { display.setCurrent(window_under); } else { MyAlert.show("Internal error", "Internal error. This can't happen.", display); } } } // AboutForm // A Form that shows, and lets the user edit, data about a Criteria object class CriteriaForm extends Form implements CommandListener { private Command save_command = new Command("Save", Command.OK, 0); private Command back_command = new Command("Back", Command.BACK, 0); private Command reset_command = new Command("Reset", Command.SCREEN, 0); private Criteria criteria; private Display display; private Displayable window_under; private TextField h_a_item; private TextField v_a_item; private TextField time_item; private ChoiceGroup power_item; private ChoiceGroup cost_item; private ChoiceGroup speed_item; private ChoiceGroup altitude_item; private ChoiceGroup address_item; public CriteriaForm(Criteria criteria, Display display, Displayable window_under) { super("Criteria"); this.criteria = criteria; this.display = display; this.window_under = window_under; h_a_item = new TextField("Horizontal accuracy (in meters)", "", 30, TextField.NUMERIC); append(h_a_item); v_a_item = new TextField("Veritical accuracy (in meters):", "", 30, TextField.NUMERIC); append(v_a_item); time_item = new TextField("Preferred response time (in seconds):", "", 30, TextField.DECIMAL); append(time_item); power_item = new ChoiceGroup("Power consumption:", ChoiceGroup.EXCLUSIVE); power_item.append("No requirements", null); power_item.append("Only low power consumption allowed", null); power_item.append("Average power consumption allowed", null); power_item.append("High power consumption allowed", null); append(power_item); cost_item = new ChoiceGroup("Cost allowed:", ChoiceGroup.MULTIPLE); cost_item.append("Yes, allowed", null); append(cost_item); speed_item = new ChoiceGroup("Speed and course required:", ChoiceGroup.MULTIPLE); speed_item.append("Yes, required", null); append(speed_item); altitude_item = new ChoiceGroup("Altitude required:", ChoiceGroup.MULTIPLE); altitude_item.append("Yes, required", null); append(altitude_item); address_item = new ChoiceGroup("Address info required:", ChoiceGroup.MULTIPLE); address_item.append("Yes, required", null); append(address_item); load_values(); addCommand(save_command); addCommand(back_command); addCommand(reset_command); setCommandListener(this); } public void setCriteria(Criteria criteria) { this.criteria = criteria; load_values(); } public void commandAction(Command c, Displayable s) { if (c == save_command) { save_values(); display.setCurrent(window_under); } else if (c == back_command) { display.setCurrent(window_under); } else if (c == reset_command) { load_values(); } else { MyAlert.show("Internal error", "This can't happen.", display); } } public void load_values() { h_a_item.setString("" + criteria.getHorizontalAccuracy()); v_a_item.setString("" + criteria.getVerticalAccuracy()); time_item.setString("" + criteria.getPreferredResponseTime() / 1000.0); int power = criteria.getPreferredPowerConsumption(); switch (power) { case Criteria.NO_REQUIREMENT: power_item.setSelectedIndex(0, true); break; case Criteria.POWER_USAGE_LOW: power_item.setSelectedIndex(1, true); break; case Criteria.POWER_USAGE_MEDIUM: power_item.setSelectedIndex(2, true); break; case Criteria.POWER_USAGE_HIGH: power_item.setSelectedIndex(3, true); break; default: MyAlert.show("Internal error", "This can't happen.", display); break; } cost_item.setSelectedIndex(0, criteria.isAllowedToCost()); speed_item.setSelectedIndex(0, criteria.isSpeedAndCourseRequired()); altitude_item.setSelectedIndex(0, criteria.isAltitudeRequired()); address_item.setSelectedIndex(0, criteria.isAddressInfoRequired()); } public void save_values() { try { criteria.setHorizontalAccuracy(Integer.parseInt(h_a_item.getString())); criteria.setVerticalAccuracy(Integer.parseInt(v_a_item.getString())); criteria.setPreferredResponseTime((int)(Double.parseDouble(time_item.getString()) * 1000)); switch (power_item.getSelectedIndex()) { case 0: criteria.setPreferredPowerConsumption(Criteria.NO_REQUIREMENT); break; case 1: criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_LOW); break; case 2: criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM); break; case 3: criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH); break; default: MyAlert.show("Internal error", "This can't happen.", display); break; } boolean[] flag_array = new boolean[1]; cost_item.getSelectedFlags(flag_array); criteria.setCostAllowed(flag_array[0]); speed_item.getSelectedFlags(flag_array); criteria.setSpeedAndCourseRequired(flag_array[0]); altitude_item.getSelectedFlags(flag_array); criteria.setAltitudeRequired(flag_array[0]); address_item.getSelectedFlags(flag_array); criteria.setAddressInfoRequired(flag_array[0]); } catch (NumberFormatException e) { MyAlert.show("NumberFormatException", "Not the right kind of number. This can't happen.", display); } catch (Throwable e) { MyAlert.show("Throwable", "Some other error. Try again.", display); } } } // CriteriaForm // The SatelliteRushTest midlet class itself public class SatelliteRushTest extends MIDlet implements CommandListener { RushCanvas canvas; private Display display; private Command exit_command = new Command("Avsluta", Command.EXIT, 0); public SatelliteRushTest() { } public void destroyApp(boolean unconditional) { } public void pauseApp() { } public void startApp() { display = Display.getDisplay(this); canvas = new RushCanvas(this); // display.setCurrent(canvas); canvas.edit_preferences(); } public void finished(long winning_time_ms) { Alert textrutan = new Alert("Du vann!", "Tid: " + (winning_time_ms + 500) / 1000 + " sekunder", null, AlertType.INFO); textrutan.setTimeout(Alert.FOREVER); textrutan.addCommand(exit_command); textrutan.setCommandListener(this); display.setCurrent(textrutan); } public void commandAction(Command c, Displayable d) { if (c == exit_command) { destroyApp(true); notifyDestroyed(); return; } } } // SatelliteRushTest class Brick { public int x, y; public int radius; public int color; } class RushCanvas extends GameCanvas implements CommandListener, YesOrNoCallback { private Command exit_command = new Command("Exit", Command.EXIT, 4); private Command about_command = new Command("About", Command.SCREEN, 3); private Command help_command = new Command("Help", Command.SCREEN, 3); private Command preferences_command = new Command("Configuration", Command.SCREEN, 2); private Display display; public void commandAction(Command c, Displayable s) { if (c == exit_command) { YesOrNoDialogue dialogue = new YesOrNoDialogue("Do you really want to quit?", this, display); display.setCurrent(dialogue); } else if (c == about_command) about(); else if (c == help_command) help(); else if (c == preferences_command) edit_preferences(); else MyAlert.show("Internal error", "Internal error. This can't happen.", display); } public void gotAnswer(boolean answer) { if (answer) { parent_midlet.notifyDestroyed(); } else { display.setCurrent(this); } } private AboutForm about_form = null; private void about() { if (about_form == null) about_form = new AboutForm(display, this); display.setCurrent(about_form); } private HelpForm help_form = null; private void help() { if (help_form == null) help_form = new HelpForm(display, this); display.setCurrent(help_form); } private Preferences preferences = new Preferences(getWidth(), getHeight()); private PreferencesForm preferences_form; public void edit_preferences() { if (preferences_form == null) preferences_form = new PreferencesForm(preferences, display, this); pause_time = System.currentTimeMillis(); display.setCurrent(preferences_form); } public final int MAX_BRICKS = 8; public final double BASE_SPEED = getWidth() / 100.0; // Thanks to: http://devlinslab.blogspot.com/2007/10/input-handling-keypress-with-repeat.html // key repeat rate in milliseconds public static final int KEY_REPEAT_RATE = 100; // 0 = as fast as possible (?) private int paddle_step = getWidth() / 20; private boolean left_is_down = false; private boolean right_is_down = false; private long left_keytick = 0; private long right_keytick = 0; private Timer timer; private int max_brick_radius; private Brick[] bricks; private int nr_bricks; double ball_x, ball_y; double ball_dx_factor, ball_dy_factor; int ball_radius; int paddle_x; int paddle_y; int paddle_width; private Graphics g; long wait_until = 0; long start_time = 0; long winning_time; Random prng = new Random(); private SatelliteRushTest parent_midlet; public RushCanvas(SatelliteRushTest parent_midlet) { super(false); // setFullScreenMode(true); this.parent_midlet = parent_midlet; display = Display.getDisplay(parent_midlet); addCommand(exit_command); addCommand(about_command); addCommand(help_command); addCommand(preferences_command); setCommandListener(this); int screen_width = getWidth(); int screen_height = getHeight(); int usable_screen_height = (int)(0.6 * (screen_height - 5)); if (usable_screen_height > screen_width) max_brick_radius = usable_screen_height / MAX_BRICKS / 2; else max_brick_radius = screen_width / MAX_BRICKS / 2; int nr_brick_rows = usable_screen_height / (2*max_brick_radius); double row_size = 1.0 * usable_screen_height / nr_brick_rows; int nr_brick_columns = screen_width / (2*max_brick_radius); double column_size = 1.0 * screen_width / nr_brick_columns; nr_bricks = nr_brick_rows * nr_brick_columns; bricks = new Brick[nr_bricks]; for (int row = 0; row < nr_brick_rows; ++row) { for (int column = 0; column < nr_brick_columns; ++column) { Brick b = new Brick(); b.radius = prng.nextInt(max_brick_radius / 2 + 1) + max_brick_radius / 2; if (b.radius > max_brick_radius) b.radius = max_brick_radius; /* b.x = (int)((column + 0.5) * column_size); b.y = (int)((row + 0.5) * row_size); */ b.x = (int)(column * column_size + b.radius + prng.nextDouble() * (column_size - 2*b.radius)); b.y = (int)(row * row_size + b.radius + prng.nextDouble() * (row_size - 2*b.radius)); b.color = prng.nextInt(0xffffff + 1); bricks[row * nr_brick_columns + column] = b; } } System.out.println("screen_width = " + screen_width); System.out.println("usable_screen_height = " + usable_screen_height); System.out.println("max_brick_radius = " + max_brick_radius); System.out.println("nr_brick_columns = " + nr_brick_columns); System.out.println("nr_brick_rows = " + nr_brick_rows); System.out.println("nr_bricks = " + nr_bricks); ball_x = screen_width / 2; ball_y = screen_height - 5 - ball_radius*2 - 30; ball_dx_factor = 1.0; ball_dy_factor = -1.0; ball_radius = 5; paddle_width = (int)preferences.getPaddleWidthPixels(); paddle_x = screen_width / 2; paddle_y = screen_height - 5; } public void showNotify() { timer = new Timer(); g = getGraphics(); TimerTask task = new TimerTask() { public void run() { if (preferences.getSatelliteMode()) process_gps_position(); else process_keys(); update_state(); render(); flushGraphics(); } }; timer.schedule(task, 0, 20); } public void hideNotify() { if (timer != null) { timer.cancel(); timer = null; } } private void process_gps_position() { int baseline_pixels = getWidth(); paddle_width = (int)preferences.getPaddleWidthPixels(); double baseline_position = preferences_form.get_baseline_position(); paddle_x = (int)(baseline_position * (baseline_pixels - paddle_width) + paddle_width / 2); } private void process_keys() { int keys = getKeyStates(); long now = System.currentTimeMillis(); left_is_down = false; if ((keys & LEFT_PRESSED) != 0) { long elapsed = now - left_keytick; if (elapsed >= KEY_REPEAT_RATE) { left_is_down = true; left_keytick = now; } } right_is_down = false; if ((keys & RIGHT_PRESSED) != 0) { long elapsed = now - right_keytick; if (elapsed >= KEY_REPEAT_RATE) { right_is_down = true; right_keytick = now; } } if (left_is_down) { paddle_x -= paddle_step; if (paddle_x < paddle_width / 2) paddle_x = paddle_width / 2; } if (right_is_down) { paddle_x += paddle_step; if (paddle_x > getWidth() - paddle_width / 2) paddle_x = getWidth() - paddle_width / 2; } } private long previous_update_time = 0; private long pause_time = 0; private void update_state() { long now = System.currentTimeMillis(); paddle_width = (int)preferences.getPaddleWidthPixels(); if (wait_until > now) return; if (start_time == 0) { start_time = now; previous_update_time = now; } // if (nr_bricks == 0) { if (nr_bricks == 0) { if (timer != null) { timer.cancel(); timer = null; } parent_midlet.finished(winning_time); return; } long period; // Number of milliseconds since last update if (pause_time != 0) { period = pause_time - previous_update_time; pause_time = 0; } else { period = now - previous_update_time; } previous_update_time = now; double base_speed_pixels = preferences.getBaseSpeedPixels(); double ball_dx = base_speed_pixels * ball_dx_factor * period / 1000.0; double ball_dy = base_speed_pixels * ball_dy_factor * period / 1000.0; ball_x += ball_dx; ball_y += ball_dy; int screen_width = getWidth(); int screen_height = getHeight(); if (ball_x + ball_radius >= screen_width) { ball_dx_factor *= (-1.005 + prng.nextDouble() / 100); // To avoid the ball getting stuck in a fixed rut ball_x = screen_width - ball_radius - 0.5; // To avoid the ball getting "stuck" at the wall } else if (ball_x - ball_radius <= 0) { ball_dx_factor *= (-1.005 + prng.nextDouble() / 100); ball_x = ball_radius + 0.5; // To avoid the ball getting "stuck" } if (ball_y - ball_radius <= 0) { ball_dy_factor *= (-1.005 + prng.nextDouble() / 100); ball_y = ball_radius + 0.5; // To avoid the ball getting "stuck" } else if (ball_y + ball_radius >= screen_height - 5) { if (ball_x + ball_radius > paddle_x - paddle_width / 2 && ball_x - ball_radius < paddle_x + paddle_width / 2) { System.out.println("Hit the paddle!"); ball_dy_factor *= (-1.005 + prng.nextDouble() / 100); ball_y = paddle_y - ball_radius - 0.5; // To avoid the ball getting "stuck" } else { System.out.println("Missed the paddle!"); /* try { Manager.playTone(69, 250, 100); } catch (MediaException me) { System.out.println("MediaException: " + me); } try { Manager.playTone(40, 1000, 100); } catch (MediaException me) { System.out.println("MediaException: " + me); } */ ball_x = screen_width / 2; ball_y = screen_height - 5 - ball_radius*2 - 30; ball_dx_factor = 1.0; ball_dy_factor = -1.0; wait_until = now + 2000; previous_update_time = wait_until; } } for (int i = 0; i < nr_bricks; ++i) { Brick b = bricks[i]; // To avoid some floating-point calculations, check a square first. if ( Math.abs(b.x - ball_x) <= b.radius + ball_radius + 1 && Math.abs(b.y - ball_y) <= b.radius + ball_radius + 1 && Math.sqrt((b.x - ball_x) * (b.x - ball_x) + (b.y - ball_y) * (b.y - ball_y)) < b.radius + ball_radius) { System.out.println("Popped a brick!"); /* try { Manager.playTone(100, 1000, 100); } catch (MediaException me) { System.out.println("MediaException: " + me); } */ double current_heading = Float11.atan(ball_dy_factor / ball_dx_factor); if (ball_dx_factor < 0) current_heading += Math.PI; // System.out.println("current_heading = " + current_heading); double normal = Float11.atan((ball_y-b.y)/(ball_x-b.x)); if ((ball_x - b.x) < 0) normal += Math.PI; double speed_factor = Math.sqrt(ball_dx_factor * ball_dx_factor + ball_dy_factor * ball_dy_factor); // Bounce back! double new_heading = 2 * normal - current_heading - Math.PI; speed_factor *= 1.03; ball_dx_factor = speed_factor * Math.cos(new_heading); ball_dy_factor = speed_factor * Math.sin(new_heading); bricks[i] = bricks[nr_bricks - 1]; nr_bricks--; // if (nr_bricks == 0) { if (nr_bricks == 0) { winning_time = now - start_time; System.out.println("Time: " + (winning_time + 500) / 1000 + " seconds!"); wait_until = now + 2000; previous_update_time = wait_until; } break; } } // Avoid ball directions in too right angles (very small x or y speed components) double base_speed = preferences.getBaseSpeedPixels(); double ball_dx_factor_abs = Math.abs(ball_dx_factor); double ball_dy_factor_abs = Math.abs(ball_dy_factor); if (ball_dx_factor_abs < ball_dy_factor_abs / 10) { if (ball_dx_factor > 0) ball_dx_factor = ball_dy_factor_abs / 10; else ball_dx_factor = -ball_dy_factor_abs / 10; } if (ball_dy_factor_abs < ball_dx_factor_abs / 10) { if (ball_dy_factor > 0) ball_dy_factor = ball_dx_factor_abs / 10; else ball_dy_factor = -ball_dx_factor_abs / 10; } // System.out.println("ball_x = " + ball_x + ", ball_y = " + ball_y + ", nr_bricks = " + nr_bricks); } private void render() { g.setColor(0xffffff); int screen_width = getWidth(); int screen_height = getHeight(); g.fillRect(0, 0, screen_width, screen_height); for (int i = 0; i < nr_bricks; ++i) { // System.out.println("Drawing i = " + i); Brick b = bricks[i]; g.setColor(b.color); g.fillArc(b.x - b.radius, b.y - b.radius, b.radius*2, b.radius*2, 0, 360); g.setColor(0x000000); g.drawArc(b.x - b.radius, b.y - b.radius, b.radius*2, b.radius*2, 0, 360); } g.setColor(0x000000); if (preferences.getShowPath()) { g.setColor(0xff0000); double speed_factor = Math.sqrt(ball_dx_factor * ball_dx_factor + ball_dy_factor * ball_dy_factor); double screen_diagonal = Math.sqrt(screen_width * screen_width + screen_height * screen_height); double speed_scale = screen_diagonal / speed_factor; g.drawLine((int)ball_x, (int)ball_y, (int)(ball_x + ball_dx_factor * speed_scale), (int)(ball_y + ball_dy_factor * speed_scale)); g.setColor(0x000000); } g.fillArc((int)(ball_x - ball_radius), (int)(ball_y - ball_radius), ball_radius * 2, ball_radius * 2, 0, 360); paddle_width = (int)preferences.getPaddleWidthPixels(); g.fillRect(paddle_x - paddle_width / 2, paddle_y, paddle_width, 5); } } // class RushCanvas