import java.awt.*; import java.awt.event.*; import java.lang.*; import java.applet.*; // // main applet class // public class SpeedRacer extends Applet { public void init() { setSize(400, 400); setLayout(new BorderLayout()); SkyCanvas sky = new SkyCanvas(400, 100); add(sky, "North"); DashCanvas dash = new DashCanvas(400, 100, this); add(dash, "South"); RoadCanvas road = new RoadCanvas(400, 200, dash, this); add(road, "Center"); } } // // Canvas for stuff above the horizon // class SkyCanvas extends Canvas { int xdim, ydim; public SkyCanvas(int x, int y) { xdim = x; ydim = y; } public void addNotify() { super.addNotify(); setSize(xdim, ydim); } public void paint(Graphics g) { g.setColor(Color.blue); g.fillRect(0, 0, xdim, ydim); } } // // Canvas for stuff on the dash // class DashCanvas extends Canvas { int xdim, ydim; int speed; int damage; int angel; int devil; int time_left; Image halo, trident; Applet mom; Image db; MediaTracker tracker; Font dash_font; public DashCanvas(int x, int y, Applet mom) { xdim = x; ydim = y; speed = angel = devil = 0; damage = 0; time_left = 0; this.mom = mom; dash_font = new Font("Helvetica", Font.ITALIC, 20); tracker = new MediaTracker(mom); halo = mom.getImage(mom.getDocumentBase(), "angel.gif"); trident = mom.getImage(mom.getDocumentBase(), "devil.gif"); tracker.addImage(halo, 0); tracker.addImage(trident, 1); } public void addNotify() { super.addNotify(); setSize(xdim, ydim); db = createImage(xdim, ydim); // double buffer image try { tracker.waitForAll(); // get images } catch (InterruptedException e) { System.out.println(e); } } public void update(Graphics g) { // no flicker paint(g); } public void paint(Graphics g) { Graphics g2 = db.getGraphics(); g2.setColor(Color.lightGray); g2.fillRect(0, 0, xdim, ydim); g2.setColor(Color.black); g2.setFont(dash_font); FontMetrics fm = g2.getFontMetrics(); // speed indicator int height = fm.getAscent() + fm.getDescent();// + fm.getLeading(); g2.drawString("Speed: " + speed, 10, height); // damage indicator if (damage > 100) damage = 100; g2.drawString("Damage: ", 10, height*2); int offset = fm.stringWidth("Damage: "); g2.setColor(Color.blue); int x1 = offset+10; int y1 = height*2-height+fm.getDescent(); int w = xdim-10-x1; int h = height*2-y1; g2.drawRect(x1, y1, w, h); g2.fillRect(x1, y1, w*damage/100, h); int iconx, icony; // halo iconx = xdim*1/3-halo.getWidth(mom); g2.drawImage(halo, iconx, height*3-height+fm.getDescent(), mom); g2.setColor(Color.black); g2.drawString(" "+angel, iconx+halo.getWidth(mom), height*3); // trident iconx = xdim*2/3-trident.getWidth(mom); g2.drawImage(trident, iconx, height*3-height+fm.getDescent(), mom); g2.setColor(Color.black); g2.drawString(" "+devil, iconx+trident.getWidth(mom), height*3); // score g2.drawString("Score: " + Math.abs(angel-devil), 10, height*4); // time left offset = fm.stringWidth("Time: 000"); g2.drawString("Time: " + time_left, xdim-10-offset, height*4); // buffer to front g.drawImage(db, 0, 0, mom); } } // // Canvas for the road and action! // class RoadCanvas extends Canvas { Image db; Car car; MediaTracker tracker; Applet mom; int xdim, ydim; Target target[]; Ticker ticker; DashCanvas dash; Line line[] = new Line[4]; int suggested_speed = 0; int suggested_xpos = 0; int x_acceleration = 0; int time_left = 0; int t2tl = 30; // time to countdown timer another notch boolean game_over = true; static int GOODPERSON = 0; // indices for target[] static int BADPERSON = 1; static int BARRIER = 2; public RoadCanvas(int x, int y, DashCanvas dash, Applet mom) { this.mom = mom; this.dash = dash; this.xdim = x; this.ydim = y; ticker = new Ticker(this); // get me a car and load the image car = new Car(); car.y = ydim - 70; // give me lines in the road for(int i = 0; i < line.length; i++) line[i] = new Line(i*i*15+3, 10, ydim); // give me some targets target = new Target[3]; target[GOODPERSON] = new Target(mom, "good1.gif", "good2.gif", "deadgood.gif", 3, 20, 0, 3, 1, 0, false); target[BADPERSON] = new Target(mom, "bad1.gif", "bad2.gif", "deadgood.gif", 3, 20, 0, 3, 0, 1, false); target[BARRIER] = new Target(mom, "barr.gif", null, "deadbarr.gif", 999, 999, 15, 15, 0, 0, true); car.image = mom.getImage(mom.getDocumentBase(), "car.gif"); tracker = new MediaTracker(mom); tracker.addImage(car.image, 0); addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (game_over) { game_over = false; car.damage = 0; car.goodkills = 0; car.badkills = 0; t2tl = 30; time_left = 100; } } }); // get ready to handle keyboard input! requestFocus(); addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent event) { switch(event.getKeyCode()) { case KeyEvent.VK_LEFT: if (car.speed > 0) car.x-=car.turnspeed; break; case KeyEvent.VK_RIGHT: if (car.speed > 0) car.x+=car.turnspeed; break; case KeyEvent.VK_UP: // fall thru car.speed++; break; case KeyEvent.VK_DOWN: car.speed--; if (car.speed < 0) car.speed = 0; break; default: System.out.println("Some key event"); break; } } }); // use the mouse for driving addMouseMotionListener(new MouseMotionAdapter() { public void mouseMoved(MouseEvent m) { suggested_speed = (ydim - m.getY()-30)/3; if (suggested_speed < 0) suggested_speed = 0; suggested_xpos = m.getX() - xdim/2; x_acceleration = Math.abs(car.x - suggested_xpos) / 5; if (x_acceleration > car.speed) x_acceleration = car.speed; } }); // gimme a nice crosshair for aiming setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); // get updates every so often ticker.start(); } public void addNotify() { super.addNotify(); setSize(xdim, ydim); db = createImage(xdim, ydim); try { tracker.waitForAll(); } catch (InterruptedException e) { System.out.println(e); } } public void update(Graphics g) { // no flicker paint(g); } public void paint(Graphics g) { // this is the only place in the universe we can put this // request focus (not including event handlers) and have // it work right. If it's not here, focus will never ever // be gained in a zillion years and no key events will be // caught. Everywhere else, this call is seemingly // ignored. requestFocus(); Graphics g2 = db.getGraphics(); // draw to double buffer g2.setColor(Color.green); // draw land g2.fillRect(0, 0, xdim, ydim); g2.setColor(Color.darkGray); // draw road int xpoints[] = new int[4]; int ypoints[] = new int[4]; xpoints[0] = xdim/2-5; ypoints[0] = 0; // top constant xpoints[1] = xdim/2+5; ypoints[1] = 0; xpoints[2] = xdim - 60 - car.x; ypoints[2] = ydim; // bottom moves xpoints[3] = 0 + 60 - car.x; ypoints[3] = ydim; g2.fillPolygon(xpoints, ypoints, 4); int bottomcenter = ydim - car.x; // draw a dashed line for(int i = 0; i < line.length; i++) { g2.setColor(Color.white); int x1 = (int)(line[i].y1 * (bottomcenter - xdim/2) / ydim + ydim); int x2 = (int)(line[i].y2 * (bottomcenter - xdim/2) / ydim + ydim); g2.drawLine(x1, (int)(line[i].y1), x2, (int)(line[i].y2)); } // draw objects for(int i = 0; i < target.length; i++) { Target t = target[i]; if (t.y >= 0) { int visx = (int)t.y * (bottomcenter+t.x - (xdim/2)) / ydim + ydim; int wid = t.image[t.current].getWidth(mom); int hei = t.image[t.current].getHeight(mom); float sf = (float)(((int)t.y+hei)/(float)ydim); // scale factor g2.drawImage(t.image[t.current], visx, (int)t.y, visx+(int)(wid*sf), (int)t.y+(int)(hei*sf), 0, 0, wid, hei, mom); } } g2.drawImage(car.image, xdim/2-32, car.y, mom); // draw car // draw game over sign if (game_over) { g2.setFont(new Font("Helvetica", Font.ITALIC | Font.BOLD, 23)); FontMetrics fm = g2.getFontMetrics(); int width = fm.stringWidth("Game Over"); int height = fm.getAscent() + fm.getDescent(); g2.setColor(Color.white); g2.drawString("Game Over", (xdim-width)/2, height*2); g2.setColor(Color.black); g2.drawString("Game Over", (xdim-width)/2-1, height*2-1); g2.setFont(new Font("Helvetica", Font.BOLD | Font.ITALIC, 13)); fm = g2.getFontMetrics(); width = fm.stringWidth("Click to Start"); height = height*2 + (fm.getAscent() + fm.getDescent())*2; g2.setColor(Color.white); g2.drawString("Click to Start", (xdim-width)/2, height); g2.setColor(Color.black); g2.drawString("Click to Start", (xdim-width)/2-1, height-1); } // finally slap that on the screen g.drawImage(db, 0, 0, mom); } // time to do some stuff! public void tick() { //move the stuff for(int i = 0; i < target.length; i++) target[i].update(ydim, car.speed); // move the lines down the street for(int i = 0; i < line.length; i++) { line[i].y1 += (line[i].y1+1)/(float)ydim * car.speed; line[i].y2 += (line[i].y2+1)/(float)ydim * car.speed; if (line[i].y1 > ydim) line[i].reset(); } // move the car car.dist += car.speed; if (car.y < ydim - 70) car.y++; // bump! if (game_over) suggested_speed = 0; // accelerate and steer if (car.speed < suggested_speed) car.speed++; else if (car.speed > suggested_speed) car.speed--; if (car.speed > 0) { if (car.x > suggested_xpos) car.x -= x_acceleration; if (car.x < suggested_xpos) car.x += x_acceleration; } dash.speed = car.speed * 2; dash.damage = car.damage; dash.angel = car.badkills; dash.devil = car.goodkills; dash.time_left = time_left; // see what we hit int x1 = car.x-32; // -32 cause car is centered on screen int x2 = x1+car.image.getWidth(mom); int y1 = car.y; int y2 = y1+car.image.getHeight(mom); boolean hitsomething = false; for(int i = 0; i < target.length; i++) { if (target[i].collision(x1, y1, x2, y2) && car.speed > 0) { if (target[i].current < target[i].image.length - 1) { car.speed -= target[i].slowdown; if (car.speed < 1) car.speed = 1; car.damage += target[i].damage; car.goodkills += target[i].goodworth; car.badkills += target[i].badworth; } target[i].current = target[i].image.length - 1; // change to dead image hitsomething = true; } } if (hitsomething) if (car.speed > 0 && car.y == ydim - 70) car.y -= 2; // make a bump if (car.damage >= 100) game_over = true; // decrement timer if (!game_over && time_left > 0) { t2tl--; if (t2tl <= 0) { time_left--; t2tl = 30; } } else game_over = true; // outta time repaint(); // show the new things! dash.repaint(); } } class Car { int speed; int x; int y; int goodkills; int badkills; int dist; int turnspeed; int damage; Image image; public Car() { speed = dist = x = goodkills = badkills = 0; image = null; turnspeed = 5; damage = 0; } } // // Hey! There's something in the road. // class Target { int x; float y; int deltax; // change in x pos each round int time2move; // time until deltax influences x pos Image image[]; // a some images to cycle through int current; // index of image on screen; if last, it's dead int time2shift; // time until image change boolean fixed; // true if we shouldn't move this guy at all Applet mom; int t2m_top; // don't change these--use them to reset int t2s_top; int damage; // how much it hurts to hit int slowdown; // how much it slows us int goodworth; int badworth; static int maxx = (400-120)/2; // max road x value public Target(Applet mom, String image1, String image2, String dead, int t2m, int t2s, int damage, int slow, int goodworth, int badworth, boolean fixed) { this.mom = mom; t2m_top = t2m; t2s_top = t2s; this.fixed = fixed; this.damage = damage; this.slowdown = slow; this.goodworth = goodworth; this.badworth = badworth; MediaTracker mt = new MediaTracker(mom); // final image is the dead image if (image2 != null) { image = new Image[3]; image[0] = mom.getImage(mom.getDocumentBase(), image1); mt.addImage(image[0], 0); image[1] = mom.getImage(mom.getDocumentBase(), image2); mt.addImage(image[1], 1); image[2] = mom.getImage(mom.getDocumentBase(), dead); mt.addImage(image[2], 2); } else { image = new Image[2]; image[0] = mom.getImage(mom.getDocumentBase(), image1); mt.addImage(image[0], 0); image[1] = mom.getImage(mom.getDocumentBase(), dead); mt.addImage(image[1], 1); } try { mt.waitForAll(); } catch (InterruptedException e) { System.out.println(e); } resetRand(); } public void resetRand() { x = (int)(Math.random() * (maxx*2) - maxx); y = 0; deltax = (int)(Math.random()>0.5?-1:1); time2move = t2m_top; time2shift = t2s_top; current = 0; y = (int)(Math.random() * -200); } public void update(int ydim, int speed) { // move the target down the screen: y += (Math.abs(y)+1)/(float)ydim * speed; if (y > ydim) resetRand(); if (!fixed && current != image.length - 1) { time2move--; if (time2move <= 0) { x += deltax; time2move = t2m_top; } } // update the current images, if necessary: time2shift--; if (time2shift <= 0) { if (current < image.length-1) { // !dead current++; if (current == image.length-1) current = 0; // don't show the dead image } time2shift = t2s_top; } } // collision code disregards scale factor. But the images are so // close to full size when they reach the car it makes no //difference. public boolean collision(int a1, int b1, int a2, int b2) { // collision checking int x1 = x; int x2 = x1+image[current].getWidth(mom); int y1 = (int)y; int y2 = y1+image[current].getHeight(mom); if ((x1 >= a1 && x1 <= a2 && y1 >= b1 && y1 <= b2) || (x2 >= a1 && x2 <= a2 && y1 >= b1 && y1 <= b2)) { return true; } return false; } } // // ticker thread -- runs things in the RoadCanvas that updates things // and does stuff. // class Ticker extends Thread { static int sleep_time = 20; // milliseconds RoadCanvas mom; public Ticker(RoadCanvas mom) { this.mom = mom; } public void run() { while(true) { // forever! // sleep a bit try { this.sleep(sleep_time); } catch(Exception e) { System.out.println(e); } // notify mom! mom.tick(); } } } // // Cheesy class for line in the road // class Line { float y1, y2; int len, height; public Line(int start, int len, int height) { this.len = len; this.height = height; this.y1 = this.y2 = (float)start; for(int i=0;i