Ignore:
Timestamp:
2010-09-15T18:54:18+02:00 (15 years ago)
Author:
stoecker
Message:

remove tabs

File:
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/GameWindow.java

    r19990 r23190  
    4242
    4343public class GameWindow extends JFrame implements ActionListener {
    44         public GameWindow(Layer ground) {
    45                 setTitle("The Ultimate WMS Super-speed Turbo Challenge II");
    46                 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    47                 setUndecorated(true);
    48                 setSize(s.getScreenSize().width, s.getScreenSize().height);
    49                 setLocationRelativeTo(null);
    50                 setResizable(false);
    51 
    52                 while (s.getScreenSize().width < width * scale ||
    53                                 s.getScreenSize().height < height * scale)
    54                         scale --;
    55                 add(panel);
    56 
    57                 setVisible(true);
    58 
    59                 /* TODO: "Intro" screen perhaps with "Hall of Fame" */
    60 
    61                 screen_image = new BufferedImage(width, height,
    62                                 BufferedImage.TYPE_INT_RGB);
    63                 screen = screen_image.getGraphics();
    64 
    65                 this.ground = ground;
    66                 ground_view = new fake_map_view(Main.map.mapView, 0.0000001);
    67 
    68                 /* Retrieve start position */
    69                 EastNorth start = ground_view.parent.getCenter();
    70                 lat = start.north();
    71                 lon = start.east();
    72 
    73                 addKeyListener(new TAdapter());
    74 
    75                 timer = new Timer(80, this);
    76                 timer.start();
    77 
    78                 car_gps = new gps();
    79                 car_gps.start();
    80 
    81                 car_engine = new engine();
    82                 car_engine.start();
    83 
    84                 for (int i = 0; i < maxsprites; i ++)
    85                         sprites[i] = new sprite_pos();
    86 
    87                 generate_sky();
    88         }
    89 
    90         protected engine car_engine;
    91 
    92         protected gps car_gps;
    93         protected class gps extends Timer implements ActionListener {
    94                 public gps() {
    95                         super(1000, null);
    96                         addActionListener(this);
    97 
    98                         trackSegs = new ArrayList<Collection<WayPoint>>();
    99                 }
    100 
    101                 protected Collection<WayPoint> segment;
    102                 protected Collection<Collection<WayPoint>> trackSegs;
    103 
    104                 public void actionPerformed(ActionEvent e) {
    105                         /* We should count the satellites here, see if we
    106                         * have a fix and add any distortions.  */
    107 
    108                         segment.add(new WayPoint(Main.proj.eastNorth2latlon(
    109                                         new EastNorth(lon, lat))));
    110                 }
    111 
    112                 public void start() {
    113                         super.start();
    114 
    115                         /* Start recording */
    116                         segment = new ArrayList<WayPoint>();
    117                         trackSegs.add(segment);
    118                         actionPerformed(null);
    119                 }
    120 
    121                 public void save_trace() {
    122                         int len = 0;
    123                         for (Collection<WayPoint> seg : trackSegs)
    124                                 len += seg.size();
    125 
    126                         /* Don't save traces shorter than 5s */
    127                         if (len <= 5)
    128                                 return;
    129 
    130                         GpxData data = new GpxData();
    131                         data.tracks.add(new ImmutableGpxTrack(trackSegs,
    132                                                 new HashMap<String, Object>()));
    133 
    134                         ground_view.parent.addLayer(
    135                                         new GpxLayer(data, "Car GPS trace"));
    136                 }
    137         }
    138 
    139         /* These are EastNorth, not actual LatLon */
    140         protected double lat, lon;
    141         /* Camera's altitude above surface (same units as lat/lon above) */
    142         protected double ele = 0.000003;
    143         /* Cut off at ~75px from bottom of the screen */
    144         protected double horizon = 0.63;
    145         /* Car's distance from the camera lens */
    146         protected double cardist = ele * 3;
    147 
    148         /* Pixels per pixel, the bigger the more oldschool :-)  */
    149         protected int scale = 5;
    150 
    151         protected BufferedImage screen_image;
    152         protected Graphics screen;
    153         protected int width = 320;
    154         protected int height = 200;
    155         protected int centre = width / 2;
    156 
    157         double maxdist = ele / (horizon - 0.6);
    158         double realwidth = maxdist * width / height;
    159         double pixelperlat = 1.0 * width / realwidth;
    160         double sratio = 0.85;
    161         protected int sw = (int) (2 * Math.PI * maxdist * pixelperlat * sratio);
    162 
    163         /* TODO: figure out how to load these dynamically after splash
    164         * screen is shown */
    165         protected static final ImageIcon car[] = new ImageIcon[] {
    166                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    167                                         WMSRacer.class.getResource(
    168                                                 "/images/car0-l.png"))),
    169                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    170                                         WMSRacer.class.getResource(
    171                                                 "/images/car0.png"))),
    172                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    173                                         WMSRacer.class.getResource(
    174                                                 "/images/car0-r.png"))),
    175                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    176                                         WMSRacer.class.getResource(
    177                                                 "/images/car1-l.png"))),
    178                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    179                                         WMSRacer.class.getResource(
    180                                                 "/images/car1.png"))),
    181                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    182                                         WMSRacer.class.getResource(
    183                                                 "/images/car1-r.png"))),
    184         };
    185         protected static final ImageIcon bg[] = new ImageIcon[] {
    186                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    187                                         WMSRacer.class.getResource(
    188                                                 "/images/bg0.png"))),
    189         };
    190         protected static final ImageIcon skyline[] = new ImageIcon[] {
    191                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    192                                         WMSRacer.class.getResource(
    193                                                 "/images/horizon.png"))),
    194         };
    195         protected static final ImageIcon cactus[] = new ImageIcon[] {
    196                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    197                                         WMSRacer.class.getResource(
    198                                                 "/images/cactus0.png"))),
    199                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    200                                         WMSRacer.class.getResource(
    201                                                 "/images/cactus1.png"))),
    202                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    203                                         WMSRacer.class.getResource(
    204                                                 "/images/cactus2.png"))),
    205         };
    206         protected static final ImageIcon cloud[] = new ImageIcon[] {
    207                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    208                                         WMSRacer.class.getResource(
    209                                                 "/images/cloud0.png"))),
    210                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    211                                         WMSRacer.class.getResource(
    212                                                 "/images/cloud1.png"))),
    213                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    214                                         WMSRacer.class.getResource(
    215                                                 "/images/cloud2.png"))),
    216                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    217                                         WMSRacer.class.getResource(
    218                                                 "/images/cloud3.png"))),
    219                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    220                                         WMSRacer.class.getResource(
    221                                                 "/images/cloud4.png"))),
    222         };
    223         protected static final ImageIcon aircraft[] = new ImageIcon[] {
    224                 new ImageIcon(Toolkit.getDefaultToolkit().createImage(
    225                                         WMSRacer.class.getResource(
    226                                                 "/images/aircraft0.png"))),
    227         };
    228         protected static final ImageIcon loading = new ImageIcon(
    229                         Toolkit.getDefaultToolkit().createImage(
    230                                         WMSRacer.class.getResource(
    231                                                 "/images/loading.png")));
    232         protected static Toolkit s = Toolkit.getDefaultToolkit();
    233         protected int current_bg = 0;
    234         protected int current_car = 0;
    235         protected boolean cacti_on = true;
    236         protected List<EastNorth> cacti = new ArrayList<EastNorth>();
    237         protected List<EastNorth> todelete = new ArrayList<EastNorth>();
    238         protected int splashframe = -1;
    239         protected EastNorth splashcactus;
    240 
    241         protected Layer ground;
    242         protected double heading = 0.0;
    243         protected double wheelangle = 0.0;
    244         protected double speed = 0.0;
    245         protected boolean key_down[] = new boolean[] {
    246                 false, false, false, false, };
    247 
    248         protected void move() {
    249                 /* Left */
    250                 /* (At high speeds make more gentle turns) */
    251                 if (key_down[0])
    252                         wheelangle -= 0.1 / (1.0 + Math.abs(speed));
    253                 /* Right */
    254                 if (key_down[1])
    255                         wheelangle += 0.1 / (1.0 + Math.abs(speed));
    256                 if (wheelangle > 0.3)
    257                         wheelangle = 0.3; /* Radians */
    258                 if (wheelangle < -0.3)
    259                         wheelangle = -0.3;
    260 
    261                 wheelangle *= 0.7;
    262 
    263                 /* Up */
    264                 if (key_down[2])
    265                         speed += speed >= 0.0 ? 1.0 / (2.0 + speed) : 0.5;
    266                 /* Down */
    267                 if (key_down[3]) {
    268                         if (speed >= 0.5) /* Brake (TODO: sound) */
    269                                 speed -= 0.5;
    270                         else if (speed >= 0.01) /* Brake (TODO: sound) */
    271                                 speed = 0.0;
    272                         else /* Reverse */
    273                                 speed -= 0.5 / (4.0 - speed);
    274                 }
    275 
    276                 speed *= 0.97;
    277                 car_engine.set_speed(speed);
    278 
    279                 if (speed > -0.1 && speed < 0.1)
    280                         speed = 0;
    281 
    282                 heading += wheelangle * speed;
    283 
    284                 boolean chop = false;
    285                 double newlat = lat + Math.cos(heading) * speed * ele * 0.2;
    286                 double newlon = lon + Math.sin(heading) * speed * ele * 0.2;
    287                 for (EastNorth pos : cacti) {
    288                         double alat = Math.abs(pos.north() - newlat);
    289                         double alon = Math.abs(pos.east() - newlon);
    290                         if (alat + alon < ele * 1.0) {
    291                                 if (Math.abs(speed) < 2.0) {
    292                                         if (speed > 0.0)
    293                                                 speed = -0.5;
    294                                         else
    295                                                 speed = 0.3;
    296                                         newlat = lat;
    297                                         newlon = lon;
    298                                         break;
    299                                 }
    300 
    301                                 chop = true;
    302                                 splashframe = 0;
    303                                 splashcactus = pos;
    304                                 todelete.add(pos);
    305                         }
    306                 }
    307 
    308                 lat = newlat;
    309                 lon = newlon;
    310 
    311                 /* Seed a new cactus if we're moving.
    312                 * TODO: hook into data layers and avoid putting the cactus on
    313                 * the road!
    314                 */
    315                 if (cacti_on && Math.random() * 30.0 < speed) {
    316                         double left_x = maxdist * (width - centre) / height;
    317                         double right_x = maxdist * (0 - centre) / height;
    318                         double x = left_x + Math.random() * (right_x - left_x);
    319                         double clat = lat + (maxdist - cardist) *
    320                                 Math.cos(heading) - x * Math.sin(heading);
    321                         double clon = lon + (maxdist - cardist) *
    322                                 Math.sin(heading) + x * Math.cos(heading);
    323 
    324                         cacti.add(new EastNorth(clon, clat));
    325                         chop = true;
    326                 }
    327 
    328                 /* Chop down any cactus far enough that it can't
    329                 * be seen.  ``If a cactus falls in a forest and
    330                 * there is nobody around did it make a sound?''
    331                 */
    332                 if (chop) {
    333                         for (EastNorth pos : cacti) {
    334                                 double alat = Math.abs(pos.north() - lat);
    335                                 double alon = Math.abs(pos.east() - lon);
    336                                 if (alat + alon > 2 * maxdist)
    337                                         todelete.add(pos);
    338                         }
    339                         cacti.removeAll(todelete);
    340                         todelete = new ArrayList<EastNorth>();
    341                 }
    342         }
    343 
    344         int frame;
    345         boolean downloading = false;
    346         protected void screen_repaint() {
    347                 /* Draw background first */
    348                 sky_paint();
    349 
    350                 /* On top of it project the floor */
    351                 ground_paint();
    352 
    353                 /* Messages */
    354                 frame ++;
    355                 if ((frame & 8) == 0 && downloading)
    356                         screen.drawImage(loading.getImage(), centre -
    357                                         loading.getIconWidth() / 2, 50, this);
    358 
    359                 /* Sprites */
    360                 sprites_paint();
    361         }
    362 
    363         static double max3(double x[]) {
    364                 return x[0] > x[1] ? x[2] > x[0] ? x[2] : x[0] :
    365                         (x[2] > x[1] ? x[2] : x[1]);
    366         }
    367         static double min3(double x[]) {
    368                 return x[0] < x[1] ? x[2] < x[0] ? x[2] : x[0] :
    369                         (x[2] < x[1] ? x[2] : x[1]);
    370         }
    371 
    372         protected void ground_paint() {
    373                 double sin = Math.sin(heading);
    374                 double cos = Math.cos(heading);
    375 
    376                 /* First calculate the bounding box for the visible area.
    377                 * The area will be (nearly) a triangle, so calculate the
    378                 * EastNorth for the three corners and make a bounding box.
    379                 */
    380                 double left_x = maxdist * (width - centre) / height;
    381                 double right_x = maxdist * (0 - centre) / height;
    382                 double e_lat[] = new double[] {
    383                         lat + (maxdist - cardist) * cos - left_x * sin,
    384                         lat + (maxdist - cardist) * cos - right_x * sin,
    385                         lat - cardist * cos, };
    386                 double e_lon[] = new double[] {
    387                         lon + (maxdist - cardist) * sin + left_x * cos,
    388                         lon + (maxdist - cardist) * sin + right_x * cos,
    389                         lon - cardist * sin, };
    390                 ground_view.setProjectionBounds(new ProjectionBounds(
    391                                 new EastNorth(min3(e_lon), min3(e_lat)),
    392                                 new EastNorth(max3(e_lon), max3(e_lat))));
    393 
    394                 /* If the layer is a WMS layer, check if any tiles are
    395                 * missing */
    396                 if (ground instanceof wmsplugin.WMSLayer) {
    397                         wmsplugin.WMSLayer wms = (wmsplugin.WMSLayer) ground;
    398                         downloading = wms.hasAutoDownload() && (
    399                                         null == wms.findImage(new EastNorth(
    400                                                         e_lon[0], e_lat[0])) ||
    401                                         null == wms.findImage(new EastNorth(
    402                                                         e_lon[0], e_lat[0])) ||
    403                                         null == wms.findImage(new EastNorth(
    404                                                         e_lon[0], e_lat[0])));
    405                 }
    406 
    407                 /* Request the image from ground layer */
    408                 ground.paint(ground_view.graphics, ground_view, null);
    409 
    410                 for (int y = (int) (height * horizon + 0.1); y < height; y ++) {
    411                         /* Assume a 60 deg vertical Field of View when
    412                         * calculating the distance at given pixel.  */
    413                         double dist = ele / (1.0 * y / height - 0.6);
    414                         double lat_off = lat + (dist - cardist) * cos;
    415                         double lon_off = lon + (dist - cardist) * sin;
    416 
    417                         for (int x = 0; x < width; x ++) {
    418                                 double p_x = dist * (x - centre) / height;
    419 
    420                                 EastNorth en = new EastNorth(
    421                                                 lon_off + p_x * cos,
    422                                                 lat_off - p_x * sin);
    423 
    424                                 Point pt = ground_view.getPoint(en);
    425 
    426                                 int rgb = ground_view.ground_image.getRGB(
    427                                                 pt.x, pt.y);
    428                                 screen_image.setRGB(x, y, rgb);
    429                         }
    430                 }
    431         }
    432 
    433         protected BufferedImage sky_image;
    434         protected Graphics sky;
    435         public void generate_sky() {
    436                 sky_image = new BufferedImage(sw, 70,
    437                                 BufferedImage.TYPE_INT_ARGB);
    438                 sky = sky_image.getGraphics();
    439 
    440                 int n = (int) (Math.random() * sw * 0.03);
    441                 for (int i = 0; i < n; i ++) {
    442                         int t = (int) (Math.random() * 5.0);
    443                         int x = (int) (Math.random() *
    444                                         (sw - cloud[t].getIconWidth()));
    445                         int y = (int) ((1 - Math.random() * Math.random()) *
    446                                         (70 - cloud[t].getIconHeight()));
    447                         sky.drawImage(cloud[t].getImage(), x, y, this);
    448                 }
    449 
    450                 if (Math.random() < 0.5) {
    451                         int t = 0;
    452                         int x = (int) (300 + Math.random() * (sw - 500 -
    453                                                 aircraft[t].getIconWidth()));
    454                         sky.drawImage(aircraft[t].getImage(), x, 0, this);
    455                 }
    456         }
    457 
    458         public void sky_paint() {
    459                 /* for x -> 0, lim sin(x) / x = 1 */
    460                 int hx = (int) (-heading * maxdist * pixelperlat);
    461                 int hw = skyline[current_bg].getIconWidth();
    462                 hx = ((hx % hw) - hw) % hw;
    463 
    464                 int sx = (int) (-heading * maxdist * pixelperlat * sratio);
    465                 sx = ((sx % sw) - sw) % sw;
    466 
    467                 screen.drawImage(bg[current_bg].getImage(), 0, 0, this);
    468                 screen.drawImage(sky_image, sx, 50, this);
    469                 if (sw + sx < width)
    470                         screen.drawImage(sky_image, sx + sw, 50, this);
    471                 screen.drawImage(skyline[current_bg].getImage(), hx, 66, this);
    472                 if (hw + hx < width)
    473                         screen.drawImage(skyline[current_bg].getImage(),
    474                                         hx + hw, 66, this);
    475         }
    476 
    477         protected class sprite_pos implements Comparable {
    478                 double dist;
    479 
    480                 int x, y, sx, sy;
    481                 Image sprite;
    482 
    483                 public sprite_pos() {
    484                 }
    485 
    486                 public int compareTo(Object x) {
    487                         sprite_pos other = (sprite_pos) x;
    488                         return (int) ((other.dist - this.dist) * 1000000.0);
    489                 }
    490         }
    491 
    492         /* sizes decides how many zoom levels the sprites have.  We
    493         * could do just normal scalling according to distance but
    494         * that's not what old games did, they had prescaled sprites
    495         * for the different distances and you could see the feature
    496         * grow discretely as you approached it.  */
    497         protected final static int sizes = 8;
    498 
    499         protected final static int maxsprites = 32;
    500         protected sprite_pos sprites[] = new sprite_pos[maxsprites];
    501 
    502         protected void sprites_paint() {
    503                 /* The vehicle */
    504                 int orientation = (wheelangle > -0.02 ? wheelangle < 0.02 ?
    505                                 1 : 2 : 0) + current_car * 3;
    506                 sprites[0].sprite = car[orientation].getImage();
    507                 sprites[0].dist = cardist;
    508                 sprites[0].sx = car[orientation].getIconWidth();
    509                 sprites[0].x = centre - sprites[0].sx / 2;
    510                 sprites[0].sy = car[orientation].getIconHeight();
    511                 sprites[0].y = height - sprites[0].sy - 10; /* TODO */
    512 
    513                 /* The cacti */
    514                 double sin = Math.sin(-heading);
    515                 double cos = Math.cos(-heading);
    516                 int i = 1;
    517 
    518                 for (EastNorth ll : cacti) {
    519                         double clat = ll.north() - lat;
    520                         double clon = ll.east() - lon;
    521                         double dist = (clat * cos - clon * sin) + cardist;
    522                         double p_x = clat * sin + clon * cos;
    523 
    524                         if (dist * 8 <= cardist || dist > maxdist)
    525                                 continue;
    526 
    527                         int x = (int) (p_x * height / dist + centre);
    528                         int y = (int) ((ele / dist + 0.6) * height);
    529 
    530                         if (i >= maxsprites)
    531                                 break;
    532                         if (x < -10 || x > width + 10)
    533                                 continue;
    534 
    535                         int type = (((int) (ll.north() * 10000000.0) & 31) % 3);
    536                         int sx = cactus[type].getIconWidth();
    537                         int sy = cactus[type].getIconHeight();
    538 
    539                         sprite_pos pos = sprites[i ++];
    540                         pos.dist = dist;
    541                         pos.sprite = cactus[type].getImage();
    542                         pos.sx = (int) (sx * cardist * 0.7 / dist);
    543                         pos.sy = (int) (sy * cardist * 0.7 / dist);
    544                         pos.x = x - pos.sx / 2;
    545                         pos.y = y - pos.sy;
    546                 }
    547 
    548                 Arrays.sort(sprites, 0, i);
    549                 for (sprite_pos sprite : sprites)
    550                         if (i --> 0)
    551                                 screen.drawImage(sprite.sprite,
    552                                                 sprite.x, sprite.y,
    553                                                 sprite.sx, sprite.sy, this);
    554                         else
    555                                 break;
    556 
    557                 if (splashframe >= 0) {
    558                         splashframe ++;
    559                         if (splashframe >= 8)
    560                                 splashframe = -1;
    561 
    562                         int type = (((int) (splashcactus.north() *
    563                                                         10000000.0) & 31) % 3);
    564                         int sx = cactus[type].getIconWidth();
    565                         int sy = cactus[type].getIconHeight();
    566                         Image image = cactus[type].getImage();
    567 
    568                         for (i = 0; i < 50; i ++) {
    569                                 int x = (int) (Math.random() * sx);
    570                                 int y = (int) (Math.random() * sy);
    571                                 int w = (int) (Math.random() * 20);
    572                                 int h = (int) (Math.random() * 20);
    573                                 int nx = centre + splashframe * (x - sx / 2);
    574                                 int ny = height - splashframe * (sy - y);
    575                                 int nw = w + splashframe;
    576                                 int nh = h + splashframe;
    577 
    578                                 screen.drawImage(image,
    579                                                 nx, ny, nx + nw, ny + nh,
    580                                                 x, y, x + w, y + h, this);
    581                         }
    582                 }
    583         }
    584 
    585         public boolean no_super_repaint = false;
    586         protected class GamePanel extends JPanel {
    587                 public GamePanel() {
    588                         setBackground(Color.BLACK);
    589                         setDoubleBuffered(true);
    590                 }
    591 
    592                 public void paint(Graphics g) {
    593                         int w = (int) getSize().getWidth();
    594                         int h = (int) getSize().getHeight();
    595 
    596                         if (no_super_repaint)
    597                                 no_super_repaint = false;
    598                         else
    599                                 super.paint(g);
    600 
    601                         g.drawImage(screen_image, (w - width * scale) / 2,
    602                                         (h - height * scale) / 2,
    603                                         width * scale, height * scale, this);
    604 
    605                         Toolkit.getDefaultToolkit().sync();
    606                 }
    607         }
    608         JPanel panel = new GamePanel();
    609 
    610         protected void quit() {
    611                 timer.stop();
    612 
    613                 car_engine.stop();
    614 
    615                 car_gps.stop();
    616                 car_gps.save_trace();
    617 
    618                 setVisible(false);
    619                 panel = null;
    620                 screen_image = null;
    621                 screen = null;
    622                 dispose();
    623         }
    624 
    625         /*
    626         * Supposedly a thread drawing frames and sleeping in a loop is
    627         * better than for animating than swing Timers.  For the moment
    628         * I'll use a timer because I don't want to deal with all the
    629         * potential threading issues.
    630         */
    631         protected Timer timer;
    632         public void actionPerformed(ActionEvent e) {
    633                 move();
    634                 screen_repaint();
    635 
    636                 no_super_repaint = true;
    637                 panel.repaint();
    638         }
    639 
    640         protected class TAdapter extends KeyAdapter {
    641                 public void keyPressed(KeyEvent e) {
    642                         int key = e.getKeyCode();
    643 
    644                         if (key == KeyEvent.VK_LEFT && !key_down[0]) {
    645                                 wheelangle -= 0.02;
    646                                 key_down[0] = true;
    647                         }
    648 
    649                         if (key == KeyEvent.VK_RIGHT && !key_down[1]) {
    650                                 wheelangle += 0.02;
    651                                 key_down[1] = true;
    652                         }
    653 
    654                         if (key == KeyEvent.VK_UP)
    655                                 key_down[2] = true;
    656 
    657                         if (key == KeyEvent.VK_DOWN)
    658                                 key_down[3] = true;
    659 
    660                         if (key == KeyEvent.VK_ESCAPE)
    661                                 quit();
    662 
    663                         /* Toggle sound */
    664                         if (key == KeyEvent.VK_S) {
    665                                 if (car_engine.is_on())
    666                                         car_engine.stop();
    667                                 else
    668                                         car_engine.start();
    669                         }
    670 
    671                         /* Toggle cacti */
    672                         if (key == KeyEvent.VK_C) {
    673                                 cacti_on = !cacti_on;
    674                                 if (!cacti_on)
    675                                         cacti = new ArrayList<EastNorth>();
    676                         }
    677 
    678                         /* Switch vehicle */
    679                         if (key == KeyEvent.VK_V)
    680                                 if (current_car ++>= 1)
    681                                         current_car = 0;
    682                 }
    683 
    684                 public void keyReleased(KeyEvent e) {
    685                         int key = e.getKeyCode();
    686 
    687                         if (key == KeyEvent.VK_LEFT)
    688                                 key_down[0] = false;
    689 
    690                         if (key == KeyEvent.VK_RIGHT)
    691                                 key_down[1] = false;
    692 
    693                         if (key == KeyEvent.VK_UP)
    694                                 key_down[2] = false;
    695 
    696                         if (key == KeyEvent.VK_DOWN)
    697                                 key_down[3] = false;
    698                 }
    699         }
    700         protected fake_map_view ground_view;
     44    public GameWindow(Layer ground) {
     45        setTitle("The Ultimate WMS Super-speed Turbo Challenge II");
     46        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
     47        setUndecorated(true);
     48        setSize(s.getScreenSize().width, s.getScreenSize().height);
     49        setLocationRelativeTo(null);
     50        setResizable(false);
     51
     52        while (s.getScreenSize().width < width * scale ||
     53                s.getScreenSize().height < height * scale)
     54            scale --;
     55        add(panel);
     56
     57        setVisible(true);
     58
     59        /* TODO: "Intro" screen perhaps with "Hall of Fame" */
     60
     61        screen_image = new BufferedImage(width, height,
     62                BufferedImage.TYPE_INT_RGB);
     63        screen = screen_image.getGraphics();
     64
     65        this.ground = ground;
     66        ground_view = new fake_map_view(Main.map.mapView, 0.0000001);
     67
     68        /* Retrieve start position */
     69        EastNorth start = ground_view.parent.getCenter();
     70        lat = start.north();
     71        lon = start.east();
     72
     73        addKeyListener(new TAdapter());
     74
     75        timer = new Timer(80, this);
     76        timer.start();
     77
     78        car_gps = new gps();
     79        car_gps.start();
     80
     81        car_engine = new engine();
     82        car_engine.start();
     83
     84        for (int i = 0; i < maxsprites; i ++)
     85            sprites[i] = new sprite_pos();
     86
     87        generate_sky();
     88    }
     89
     90    protected engine car_engine;
     91
     92    protected gps car_gps;
     93    protected class gps extends Timer implements ActionListener {
     94        public gps() {
     95            super(1000, null);
     96            addActionListener(this);
     97
     98            trackSegs = new ArrayList<Collection<WayPoint>>();
     99        }
     100
     101        protected Collection<WayPoint> segment;
     102        protected Collection<Collection<WayPoint>> trackSegs;
     103
     104        public void actionPerformed(ActionEvent e) {
     105            /* We should count the satellites here, see if we
     106            * have a fix and add any distortions.  */
     107
     108            segment.add(new WayPoint(Main.proj.eastNorth2latlon(
     109                    new EastNorth(lon, lat))));
     110        }
     111
     112        public void start() {
     113            super.start();
     114
     115            /* Start recording */
     116            segment = new ArrayList<WayPoint>();
     117            trackSegs.add(segment);
     118            actionPerformed(null);
     119        }
     120
     121        public void save_trace() {
     122            int len = 0;
     123            for (Collection<WayPoint> seg : trackSegs)
     124                len += seg.size();
     125
     126            /* Don't save traces shorter than 5s */
     127            if (len <= 5)
     128                return;
     129
     130            GpxData data = new GpxData();
     131            data.tracks.add(new ImmutableGpxTrack(trackSegs,
     132                        new HashMap<String, Object>()));
     133
     134            ground_view.parent.addLayer(
     135                    new GpxLayer(data, "Car GPS trace"));
     136        }
     137    }
     138
     139    /* These are EastNorth, not actual LatLon */
     140    protected double lat, lon;
     141    /* Camera's altitude above surface (same units as lat/lon above) */
     142    protected double ele = 0.000003;
     143    /* Cut off at ~75px from bottom of the screen */
     144    protected double horizon = 0.63;
     145    /* Car's distance from the camera lens */
     146    protected double cardist = ele * 3;
     147
     148    /* Pixels per pixel, the bigger the more oldschool :-)  */
     149    protected int scale = 5;
     150
     151    protected BufferedImage screen_image;
     152    protected Graphics screen;
     153    protected int width = 320;
     154    protected int height = 200;
     155    protected int centre = width / 2;
     156
     157    double maxdist = ele / (horizon - 0.6);
     158    double realwidth = maxdist * width / height;
     159    double pixelperlat = 1.0 * width / realwidth;
     160    double sratio = 0.85;
     161    protected int sw = (int) (2 * Math.PI * maxdist * pixelperlat * sratio);
     162
     163    /* TODO: figure out how to load these dynamically after splash
     164    * screen is shown */
     165    protected static final ImageIcon car[] = new ImageIcon[] {
     166            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     167                    WMSRacer.class.getResource(
     168                        "/images/car0-l.png"))),
     169            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     170                    WMSRacer.class.getResource(
     171                        "/images/car0.png"))),
     172            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     173                    WMSRacer.class.getResource(
     174                        "/images/car0-r.png"))),
     175            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     176                    WMSRacer.class.getResource(
     177                        "/images/car1-l.png"))),
     178            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     179                    WMSRacer.class.getResource(
     180                        "/images/car1.png"))),
     181            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     182                    WMSRacer.class.getResource(
     183                        "/images/car1-r.png"))),
     184    };
     185    protected static final ImageIcon bg[] = new ImageIcon[] {
     186            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     187                    WMSRacer.class.getResource(
     188                        "/images/bg0.png"))),
     189    };
     190    protected static final ImageIcon skyline[] = new ImageIcon[] {
     191            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     192                    WMSRacer.class.getResource(
     193                        "/images/horizon.png"))),
     194    };
     195    protected static final ImageIcon cactus[] = new ImageIcon[] {
     196            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     197                    WMSRacer.class.getResource(
     198                        "/images/cactus0.png"))),
     199            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     200                    WMSRacer.class.getResource(
     201                        "/images/cactus1.png"))),
     202            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     203                    WMSRacer.class.getResource(
     204                        "/images/cactus2.png"))),
     205    };
     206    protected static final ImageIcon cloud[] = new ImageIcon[] {
     207            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     208                    WMSRacer.class.getResource(
     209                        "/images/cloud0.png"))),
     210            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     211                    WMSRacer.class.getResource(
     212                        "/images/cloud1.png"))),
     213            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     214                    WMSRacer.class.getResource(
     215                        "/images/cloud2.png"))),
     216            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     217                    WMSRacer.class.getResource(
     218                        "/images/cloud3.png"))),
     219            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     220                    WMSRacer.class.getResource(
     221                        "/images/cloud4.png"))),
     222    };
     223    protected static final ImageIcon aircraft[] = new ImageIcon[] {
     224            new ImageIcon(Toolkit.getDefaultToolkit().createImage(
     225                    WMSRacer.class.getResource(
     226                        "/images/aircraft0.png"))),
     227    };
     228    protected static final ImageIcon loading = new ImageIcon(
     229            Toolkit.getDefaultToolkit().createImage(
     230                    WMSRacer.class.getResource(
     231                        "/images/loading.png")));
     232    protected static Toolkit s = Toolkit.getDefaultToolkit();
     233    protected int current_bg = 0;
     234    protected int current_car = 0;
     235    protected boolean cacti_on = true;
     236    protected List<EastNorth> cacti = new ArrayList<EastNorth>();
     237    protected List<EastNorth> todelete = new ArrayList<EastNorth>();
     238    protected int splashframe = -1;
     239    protected EastNorth splashcactus;
     240
     241    protected Layer ground;
     242    protected double heading = 0.0;
     243    protected double wheelangle = 0.0;
     244    protected double speed = 0.0;
     245    protected boolean key_down[] = new boolean[] {
     246        false, false, false, false, };
     247
     248    protected void move() {
     249        /* Left */
     250        /* (At high speeds make more gentle turns) */
     251        if (key_down[0])
     252            wheelangle -= 0.1 / (1.0 + Math.abs(speed));
     253        /* Right */
     254        if (key_down[1])
     255            wheelangle += 0.1 / (1.0 + Math.abs(speed));
     256        if (wheelangle > 0.3)
     257            wheelangle = 0.3; /* Radians */
     258        if (wheelangle < -0.3)
     259            wheelangle = -0.3;
     260
     261        wheelangle *= 0.7;
     262
     263        /* Up */
     264        if (key_down[2])
     265            speed += speed >= 0.0 ? 1.0 / (2.0 + speed) : 0.5;
     266        /* Down */
     267        if (key_down[3]) {
     268            if (speed >= 0.5) /* Brake (TODO: sound) */
     269                speed -= 0.5;
     270            else if (speed >= 0.01) /* Brake (TODO: sound) */
     271                speed = 0.0;
     272            else /* Reverse */
     273                speed -= 0.5 / (4.0 - speed);
     274        }
     275
     276        speed *= 0.97;
     277        car_engine.set_speed(speed);
     278
     279        if (speed > -0.1 && speed < 0.1)
     280            speed = 0;
     281
     282        heading += wheelangle * speed;
     283
     284        boolean chop = false;
     285        double newlat = lat + Math.cos(heading) * speed * ele * 0.2;
     286        double newlon = lon + Math.sin(heading) * speed * ele * 0.2;
     287        for (EastNorth pos : cacti) {
     288            double alat = Math.abs(pos.north() - newlat);
     289            double alon = Math.abs(pos.east() - newlon);
     290            if (alat + alon < ele * 1.0) {
     291                if (Math.abs(speed) < 2.0) {
     292                    if (speed > 0.0)
     293                        speed = -0.5;
     294                    else
     295                        speed = 0.3;
     296                    newlat = lat;
     297                    newlon = lon;
     298                    break;
     299                }
     300
     301                chop = true;
     302                splashframe = 0;
     303                splashcactus = pos;
     304                todelete.add(pos);
     305            }
     306        }
     307
     308        lat = newlat;
     309        lon = newlon;
     310
     311        /* Seed a new cactus if we're moving.
     312        * TODO: hook into data layers and avoid putting the cactus on
     313        * the road!
     314        */
     315        if (cacti_on && Math.random() * 30.0 < speed) {
     316            double left_x = maxdist * (width - centre) / height;
     317            double right_x = maxdist * (0 - centre) / height;
     318            double x = left_x + Math.random() * (right_x - left_x);
     319            double clat = lat + (maxdist - cardist) *
     320                Math.cos(heading) - x * Math.sin(heading);
     321            double clon = lon + (maxdist - cardist) *
     322                Math.sin(heading) + x * Math.cos(heading);
     323
     324            cacti.add(new EastNorth(clon, clat));
     325            chop = true;
     326        }
     327
     328        /* Chop down any cactus far enough that it can't
     329        * be seen.  ``If a cactus falls in a forest and
     330        * there is nobody around did it make a sound?''
     331        */
     332        if (chop) {
     333            for (EastNorth pos : cacti) {
     334                double alat = Math.abs(pos.north() - lat);
     335                double alon = Math.abs(pos.east() - lon);
     336                if (alat + alon > 2 * maxdist)
     337                    todelete.add(pos);
     338            }
     339            cacti.removeAll(todelete);
     340            todelete = new ArrayList<EastNorth>();
     341        }
     342    }
     343
     344    int frame;
     345    boolean downloading = false;
     346    protected void screen_repaint() {
     347        /* Draw background first */
     348        sky_paint();
     349
     350        /* On top of it project the floor */
     351        ground_paint();
     352
     353        /* Messages */
     354        frame ++;
     355        if ((frame & 8) == 0 && downloading)
     356            screen.drawImage(loading.getImage(), centre -
     357                    loading.getIconWidth() / 2, 50, this);
     358
     359        /* Sprites */
     360        sprites_paint();
     361    }
     362
     363    static double max3(double x[]) {
     364        return x[0] > x[1] ? x[2] > x[0] ? x[2] : x[0] :
     365            (x[2] > x[1] ? x[2] : x[1]);
     366    }
     367    static double min3(double x[]) {
     368        return x[0] < x[1] ? x[2] < x[0] ? x[2] : x[0] :
     369            (x[2] < x[1] ? x[2] : x[1]);
     370    }
     371
     372    protected void ground_paint() {
     373        double sin = Math.sin(heading);
     374        double cos = Math.cos(heading);
     375
     376        /* First calculate the bounding box for the visible area.
     377        * The area will be (nearly) a triangle, so calculate the
     378        * EastNorth for the three corners and make a bounding box.
     379        */
     380        double left_x = maxdist * (width - centre) / height;
     381        double right_x = maxdist * (0 - centre) / height;
     382        double e_lat[] = new double[] {
     383            lat + (maxdist - cardist) * cos - left_x * sin,
     384            lat + (maxdist - cardist) * cos - right_x * sin,
     385            lat - cardist * cos, };
     386        double e_lon[] = new double[] {
     387            lon + (maxdist - cardist) * sin + left_x * cos,
     388            lon + (maxdist - cardist) * sin + right_x * cos,
     389            lon - cardist * sin, };
     390        ground_view.setProjectionBounds(new ProjectionBounds(
     391                new EastNorth(min3(e_lon), min3(e_lat)),
     392                new EastNorth(max3(e_lon), max3(e_lat))));
     393
     394        /* If the layer is a WMS layer, check if any tiles are
     395        * missing */
     396        if (ground instanceof wmsplugin.WMSLayer) {
     397            wmsplugin.WMSLayer wms = (wmsplugin.WMSLayer) ground;
     398            downloading = wms.hasAutoDownload() && (
     399                    null == wms.findImage(new EastNorth(
     400                            e_lon[0], e_lat[0])) ||
     401                    null == wms.findImage(new EastNorth(
     402                            e_lon[0], e_lat[0])) ||
     403                    null == wms.findImage(new EastNorth(
     404                            e_lon[0], e_lat[0])));
     405        }
     406
     407        /* Request the image from ground layer */
     408        ground.paint(ground_view.graphics, ground_view, null);
     409
     410        for (int y = (int) (height * horizon + 0.1); y < height; y ++) {
     411            /* Assume a 60 deg vertical Field of View when
     412            * calculating the distance at given pixel.  */
     413            double dist = ele / (1.0 * y / height - 0.6);
     414            double lat_off = lat + (dist - cardist) * cos;
     415            double lon_off = lon + (dist - cardist) * sin;
     416
     417            for (int x = 0; x < width; x ++) {
     418                double p_x = dist * (x - centre) / height;
     419
     420                EastNorth en = new EastNorth(
     421                        lon_off + p_x * cos,
     422                        lat_off - p_x * sin);
     423
     424                Point pt = ground_view.getPoint(en);
     425
     426                int rgb = ground_view.ground_image.getRGB(
     427                        pt.x, pt.y);
     428                screen_image.setRGB(x, y, rgb);
     429            }
     430        }
     431    }
     432
     433    protected BufferedImage sky_image;
     434    protected Graphics sky;
     435    public void generate_sky() {
     436        sky_image = new BufferedImage(sw, 70,
     437                BufferedImage.TYPE_INT_ARGB);
     438        sky = sky_image.getGraphics();
     439
     440        int n = (int) (Math.random() * sw * 0.03);
     441        for (int i = 0; i < n; i ++) {
     442            int t = (int) (Math.random() * 5.0);
     443            int x = (int) (Math.random() *
     444                    (sw - cloud[t].getIconWidth()));
     445            int y = (int) ((1 - Math.random() * Math.random()) *
     446                    (70 - cloud[t].getIconHeight()));
     447            sky.drawImage(cloud[t].getImage(), x, y, this);
     448        }
     449
     450        if (Math.random() < 0.5) {
     451            int t = 0;
     452            int x = (int) (300 + Math.random() * (sw - 500 -
     453                        aircraft[t].getIconWidth()));
     454            sky.drawImage(aircraft[t].getImage(), x, 0, this);
     455        }
     456    }
     457
     458    public void sky_paint() {
     459        /* for x -> 0, lim sin(x) / x = 1 */
     460        int hx = (int) (-heading * maxdist * pixelperlat);
     461        int hw = skyline[current_bg].getIconWidth();
     462        hx = ((hx % hw) - hw) % hw;
     463
     464        int sx = (int) (-heading * maxdist * pixelperlat * sratio);
     465        sx = ((sx % sw) - sw) % sw;
     466
     467        screen.drawImage(bg[current_bg].getImage(), 0, 0, this);
     468        screen.drawImage(sky_image, sx, 50, this);
     469        if (sw + sx < width)
     470            screen.drawImage(sky_image, sx + sw, 50, this);
     471        screen.drawImage(skyline[current_bg].getImage(), hx, 66, this);
     472        if (hw + hx < width)
     473            screen.drawImage(skyline[current_bg].getImage(),
     474                    hx + hw, 66, this);
     475    }
     476
     477    protected class sprite_pos implements Comparable {
     478        double dist;
     479
     480        int x, y, sx, sy;
     481        Image sprite;
     482
     483        public sprite_pos() {
     484        }
     485
     486        public int compareTo(Object x) {
     487            sprite_pos other = (sprite_pos) x;
     488            return (int) ((other.dist - this.dist) * 1000000.0);
     489        }
     490    }
     491
     492    /* sizes decides how many zoom levels the sprites have.  We
     493    * could do just normal scalling according to distance but
     494    * that's not what old games did, they had prescaled sprites
     495    * for the different distances and you could see the feature
     496    * grow discretely as you approached it.  */
     497    protected final static int sizes = 8;
     498
     499    protected final static int maxsprites = 32;
     500    protected sprite_pos sprites[] = new sprite_pos[maxsprites];
     501
     502    protected void sprites_paint() {
     503        /* The vehicle */
     504        int orientation = (wheelangle > -0.02 ? wheelangle < 0.02 ?
     505                1 : 2 : 0) + current_car * 3;
     506        sprites[0].sprite = car[orientation].getImage();
     507        sprites[0].dist = cardist;
     508        sprites[0].sx = car[orientation].getIconWidth();
     509        sprites[0].x = centre - sprites[0].sx / 2;
     510        sprites[0].sy = car[orientation].getIconHeight();
     511        sprites[0].y = height - sprites[0].sy - 10; /* TODO */
     512
     513        /* The cacti */
     514        double sin = Math.sin(-heading);
     515        double cos = Math.cos(-heading);
     516        int i = 1;
     517
     518        for (EastNorth ll : cacti) {
     519            double clat = ll.north() - lat;
     520            double clon = ll.east() - lon;
     521            double dist = (clat * cos - clon * sin) + cardist;
     522            double p_x = clat * sin + clon * cos;
     523
     524            if (dist * 8 <= cardist || dist > maxdist)
     525                continue;
     526
     527            int x = (int) (p_x * height / dist + centre);
     528            int y = (int) ((ele / dist + 0.6) * height);
     529
     530            if (i >= maxsprites)
     531                break;
     532            if (x < -10 || x > width + 10)
     533                continue;
     534
     535            int type = (((int) (ll.north() * 10000000.0) & 31) % 3);
     536            int sx = cactus[type].getIconWidth();
     537            int sy = cactus[type].getIconHeight();
     538
     539            sprite_pos pos = sprites[i ++];
     540            pos.dist = dist;
     541            pos.sprite = cactus[type].getImage();
     542            pos.sx = (int) (sx * cardist * 0.7 / dist);
     543            pos.sy = (int) (sy * cardist * 0.7 / dist);
     544            pos.x = x - pos.sx / 2;
     545            pos.y = y - pos.sy;
     546        }
     547
     548        Arrays.sort(sprites, 0, i);
     549        for (sprite_pos sprite : sprites)
     550            if (i --> 0)
     551                screen.drawImage(sprite.sprite,
     552                        sprite.x, sprite.y,
     553                        sprite.sx, sprite.sy, this);
     554            else
     555                break;
     556
     557        if (splashframe >= 0) {
     558            splashframe ++;
     559            if (splashframe >= 8)
     560                splashframe = -1;
     561
     562            int type = (((int) (splashcactus.north() *
     563                            10000000.0) & 31) % 3);
     564            int sx = cactus[type].getIconWidth();
     565            int sy = cactus[type].getIconHeight();
     566            Image image = cactus[type].getImage();
     567
     568            for (i = 0; i < 50; i ++) {
     569                int x = (int) (Math.random() * sx);
     570                int y = (int) (Math.random() * sy);
     571                int w = (int) (Math.random() * 20);
     572                int h = (int) (Math.random() * 20);
     573                int nx = centre + splashframe * (x - sx / 2);
     574                int ny = height - splashframe * (sy - y);
     575                int nw = w + splashframe;
     576                int nh = h + splashframe;
     577
     578                screen.drawImage(image,
     579                        nx, ny, nx + nw, ny + nh,
     580                        x, y, x + w, y + h, this);
     581            }
     582        }
     583    }
     584
     585    public boolean no_super_repaint = false;
     586    protected class GamePanel extends JPanel {
     587        public GamePanel() {
     588            setBackground(Color.BLACK);
     589            setDoubleBuffered(true);
     590        }
     591
     592        public void paint(Graphics g) {
     593            int w = (int) getSize().getWidth();
     594            int h = (int) getSize().getHeight();
     595
     596            if (no_super_repaint)
     597                no_super_repaint = false;
     598            else
     599                super.paint(g);
     600
     601            g.drawImage(screen_image, (w - width * scale) / 2,
     602                    (h - height * scale) / 2,
     603                    width * scale, height * scale, this);
     604
     605            Toolkit.getDefaultToolkit().sync();
     606        }
     607    }
     608    JPanel panel = new GamePanel();
     609
     610    protected void quit() {
     611        timer.stop();
     612
     613        car_engine.stop();
     614
     615        car_gps.stop();
     616        car_gps.save_trace();
     617
     618        setVisible(false);
     619        panel = null;
     620        screen_image = null;
     621        screen = null;
     622        dispose();
     623    }
     624
     625    /*
     626    * Supposedly a thread drawing frames and sleeping in a loop is
     627    * better than for animating than swing Timers.  For the moment
     628    * I'll use a timer because I don't want to deal with all the
     629    * potential threading issues.
     630    */
     631    protected Timer timer;
     632    public void actionPerformed(ActionEvent e) {
     633        move();
     634        screen_repaint();
     635
     636        no_super_repaint = true;
     637        panel.repaint();
     638    }
     639
     640    protected class TAdapter extends KeyAdapter {
     641        public void keyPressed(KeyEvent e) {
     642            int key = e.getKeyCode();
     643
     644            if (key == KeyEvent.VK_LEFT && !key_down[0]) {
     645                wheelangle -= 0.02;
     646                key_down[0] = true;
     647            }
     648
     649            if (key == KeyEvent.VK_RIGHT && !key_down[1]) {
     650                wheelangle += 0.02;
     651                key_down[1] = true;
     652            }
     653
     654            if (key == KeyEvent.VK_UP)
     655                key_down[2] = true;
     656
     657            if (key == KeyEvent.VK_DOWN)
     658                key_down[3] = true;
     659
     660            if (key == KeyEvent.VK_ESCAPE)
     661                quit();
     662
     663            /* Toggle sound */
     664            if (key == KeyEvent.VK_S) {
     665                if (car_engine.is_on())
     666                    car_engine.stop();
     667                else
     668                    car_engine.start();
     669            }
     670
     671            /* Toggle cacti */
     672            if (key == KeyEvent.VK_C) {
     673                cacti_on = !cacti_on;
     674                if (!cacti_on)
     675                    cacti = new ArrayList<EastNorth>();
     676            }
     677
     678            /* Switch vehicle */
     679            if (key == KeyEvent.VK_V)
     680                if (current_car ++>= 1)
     681                    current_car = 0;
     682        }
     683
     684        public void keyReleased(KeyEvent e) {
     685            int key = e.getKeyCode();
     686
     687            if (key == KeyEvent.VK_LEFT)
     688                key_down[0] = false;
     689
     690            if (key == KeyEvent.VK_RIGHT)
     691                key_down[1] = false;
     692
     693            if (key == KeyEvent.VK_UP)
     694                key_down[2] = false;
     695
     696            if (key == KeyEvent.VK_DOWN)
     697                key_down[3] = false;
     698        }
     699    }
     700    protected fake_map_view ground_view;
    701701}
Note: See TracChangeset for help on using the changeset viewer.