import java.awt.*; import java.awt.geom.*; import java.awt.event.*; import java.io.*; import java.util.Date; class LunarLanderMain { public static void main(String[] args) { LunarLanderWindow window = new LunarLanderWindow(); } } class LunarTerrain extends Polygon { // -------------------------------------------------------------------------------- // Data Members // -------------------------------------------------------------------------------- private static final int NUMBER_OF_LINES = 100, LANDING_AREA = 15, MAX_SLOPE = 4; private static final double MIN_HEIGHT = 0.06, MAX_HEIGHT = 0.50; private int flattest = 0; // -------------------------------------------------------------------------------- // Constructors // -------------------------------------------------------------------------------- public LunarTerrain(int width, int height, int initialHeight) { super(); int xchange = width / NUMBER_OF_LINES; int maxChange = xchange * MAX_SLOPE; int minHeight = (int) (height * (1 - MIN_HEIGHT)); int maxHeight = (int) (height * (1 - MAX_HEIGHT)); npoints = NUMBER_OF_LINES + 3; xpoints = new int[NUMBER_OF_LINES + 3]; ypoints = new int[NUMBER_OF_LINES + 3]; xpoints[0] = 0; ypoints[0] = initialHeight; for (int i = 1; i <= NUMBER_OF_LINES; i++) { xpoints[i] = i * xchange; ypoints[i] = ypoints[i - 1] + (int) Math.round(Math.random() * maxChange * 2) - maxChange; if (ypoints[i] > minHeight || ypoints[i] < maxHeight) { i--; } } xpoints[NUMBER_OF_LINES + 1] = width; ypoints[NUMBER_OF_LINES + 1] = height; xpoints[NUMBER_OF_LINES + 2] = 0; ypoints[NUMBER_OF_LINES + 2] = height; int difference = height; flattest = 0; for (int i = LANDING_AREA; i < NUMBER_OF_LINES - 2 * LANDING_AREA; i++) { int thisDifference = Math.abs(ypoints[i] - ypoints[i + LANDING_AREA]); if (difference > thisDifference) { flattest = i; difference = thisDifference; } } for (int i = 1; i <= LANDING_AREA; i++) { ypoints[flattest + i] = ypoints[flattest]; } } public LunarTerrain(int width, int height) { this(width, height, (int) (height * (1 - (Math.random() * (MAX_HEIGHT - MIN_HEIGHT)) - MIN_HEIGHT))); } // -------------------------------------------------------------------------------- // Methods // -------------------------------------------------------------------------------- public int getLastPoint() { return ypoints[NUMBER_OF_LINES]; } public Line2D.Double getLandingArea() { return new Line2D.Double(xpoints[flattest] + 141, ypoints[flattest], xpoints[flattest + LANDING_AREA] - 141, ypoints[flattest + LANDING_AREA]); } } class LunarLanderShapes { public static Polygon lander() { int[] xpoints = new int[] { 54, 108, 100, 141, 85, 73, 106, 89, 80, 40, 54, -54, -40, -80, -89,-106, -73, -85,-141,-100,-108, -54}; int[] ypoints = new int[] { -89, 3, 18, 89, 89, 69, 69, 38, 53, 53, 75, 75, 53, 53, 38, 69, 69, 89, 89, 18, 3, -89}; int npoints = 22; return new Polygon(xpoints, ypoints, npoints); } public static Polygon flames() { int[] xpoints = new int[] { 44, -44, 0}; int[] ypoints = new int[] { 65, 65, 200}; int npoints = 3; return new Polygon(xpoints, ypoints, npoints); } public static Polygon explosion() { int[] xpoints = new int[] { 0, 35, 112, 96, 194, 131, 224, 131, 194, 96, 112, 35, 0, -35,-112, -96,-194,-131,-244,-131,-194, -96,-112, -35}; int[] ypoints = new int[] {-224,-131,-194, -96,-112, -35, 0, 35, 112, 96, 194, 131, 224, 131, 194, 96, 112, 35, 0, -35,-112, -96,-194,-131}; int npoints = 24; return new Polygon(xpoints, ypoints, npoints); } } class LunarLanderGame { // -------------------------------------------------------------------------------- // Data Members // -------------------------------------------------------------------------------- private Shape lander, flames, transformedTerrain, life1, life2, life3; private LunarTerrain terrain; private int gravity = 1000, gravityOn = 0, thrust = 2500, thrustOn = 0, angularThrustOn = 0, terrainsPassed = 0, lastTerrain = 0, width = 8000, height = 6000, windowWidth, windowHeight, marginX = 20, marginY = 50, initialLives = 3, remainingLives = 3, landingLabelX, landingLabelY, landingScore, score = 0; private double angularThrust = 2.5, angle = 0.0, angleChange = 0.0, initialFuel = 10000.0, remainingFuel = 10000.0, xchange = 0.0, ychange = 0.0, xvelocity = 0.0, yvelocity = 0.0, xcoord = 0.0, ycoord = 0.0; private boolean resetingAngle = false, landerTouchdown = true, landerLanded = true, gameRunning = true, continueDrawing = true, endGame = false; private Image image; private Graphics2D graphics2D; private AffineTransform scaleTransform; private Color groundColor = new Color(126,96,58), groundLineColor = new Color(162,124,76), skyColor = new Color(0,0,0), landerColor = new Color(109,192,255), flamesColor = new Color(255,242,23), explosionColor1 = new Color(255,255,0), explosionColor2 = new Color(255,124,0), explosionColor3 = new Color(255,0,0), textColor = new Color(0,218,159); private Stroke line3px = new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND), line2px = new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); private Font smallFont = new Font("Arial", Font.BOLD, 12), largeFont = new Font("Arial", Font.BOLD, 26), landingFont = new Font("Arial", Font.BOLD, 18); // -------------------------------------------------------------------------------- // Constructors // -------------------------------------------------------------------------------- public LunarLanderGame(int windowWidth, int windowHeight, Image image) { this.windowWidth = windowWidth; this.windowHeight = windowHeight; height = (int) (width * (float) windowHeight / windowWidth); this.image = image; graphics2D = (Graphics2D) image.getGraphics(); graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); scaleTransform = AffineTransform.getScaleInstance((double) windowWidth / width, (double) windowHeight / height); terrain = new LunarTerrain(width, height); lander = LunarLanderShapes.lander(); flames = LunarLanderShapes.flames(); life1 = AffineTransform.getTranslateInstance(marginX + 15, marginY + 54).createTransformedShape(scaleTransform.createTransformedShape(LunarLanderShapes.lander())); life2 = AffineTransform.getTranslateInstance(marginX + 50, marginY + 54).createTransformedShape(scaleTransform.createTransformedShape(LunarLanderShapes.lander())); life3 = AffineTransform.getTranslateInstance(marginX + 85, marginY + 54).createTransformedShape(scaleTransform.createTransformedShape(LunarLanderShapes.lander())); placeOnLandingArea(); } // -------------------------------------------------------------------------------- // Methods // -------------------------------------------------------------------------------- private void placeOnLandingArea() { xvelocity = 0; yvelocity = 0; resetAngle(); Line2D landingArea = terrain.getLandingArea(); double newXcoord = (landingArea.getX1() + landingArea.getX2()) / 2; double newYcoord = landingArea.getY1() - 88.99; xchange = newXcoord - xcoord; ychange = newYcoord - ycoord; xcoord = newXcoord; ycoord = newYcoord; lander = AffineTransform.getTranslateInstance(xchange, ychange).createTransformedShape(lander); flames = AffineTransform.getTranslateInstance(xchange, ychange).createTransformedShape(flames); } private void dropToLandingArea() { xvelocity = 0; yvelocity = 0; resetAngle(); Line2D landingArea = terrain.getLandingArea(); double newYcoord = landingArea.getY1() - 188.99; ychange = newYcoord - ycoord; ycoord = newYcoord; lander = AffineTransform.getTranslateInstance(0, ychange).createTransformedShape(lander); flames = AffineTransform.getTranslateInstance(0, ychange).createTransformedShape(flames); } private void resetAngle() { lander = AffineTransform.getRotateInstance(-angle, xcoord, ycoord).createTransformedShape(lander); flames = AffineTransform.getRotateInstance(-angle, xcoord, ycoord).createTransformedShape(flames); angle = 0; } public void continueGame() { if (!gameRunning && !endGame) { if (landerLanded) { dropToLandingArea(); if (terrainsPassed != 0) { remainingFuel += (int) (.75 * initialFuel); if (remainingFuel > initialFuel) { remainingFuel = initialFuel; } terrainsPassed = 0; } } else if (landerTouchdown) { placeOnLandingArea(); remainingFuel = initialFuel; terrainsPassed = 0; } if (landerTouchdown) { while (!terrain.intersects(lander.getBounds())) { ycoord += .01; lander = AffineTransform.getTranslateInstance(0, .01).createTransformedShape(lander); flames = AffineTransform.getTranslateInstance(0, .01).createTransformedShape(flames); } } gameRunning = true; } } public void pauseGame() { gameRunning = false; } public void thrusterOn() { if (remainingFuel > 0) { thrustOn = 1; } } public void thrusterOff() { thrustOn = 0; } public void turnLanderLeft() { if (!landerLanded) { angularThrustOn = -1; } } public void turnLanderRight() { if (!landerLanded) { angularThrustOn = 1; } } public void stopLanderTurn() { angularThrustOn = 0; resetingAngle = false; } public void resetLanderTurn() { resetingAngle = true; if (angle < -0.1) { angularThrustOn = 1; } else if (angle > 0.1) { angularThrustOn = -1; } else if (angle != 0) { resetAngle(); angularThrustOn = 0; } } public void advanceTime(int increment) { if (resetingAngle) { resetLanderTurn(); } if (gameRunning) { performTransform(increment); computeVelocity(increment); if (thrustOn == 1) { remainingFuel -= increment; } if (remainingFuel < 0) { thrustOn = 0; remainingFuel = 0; } landingScore = getLandingScore(); didLanderTouchdown(); } } private void performTransform(int increment) { angleChange = angularThrustOn * angularThrust * increment / 1000.0; angle += angleChange; angle %= 2 * Math.PI; if (angle > Math.PI) { angle -= 2 * Math.PI; } else if (angle < -Math.PI) { angle += 2 * Math.PI; } xchange = .5 * (thrustOn * thrust * Math.sin(angle)) * increment * increment / 1000000 + xvelocity * increment / 1000; ychange = .5 * (gravity * gravityOn - thrustOn * thrust * Math.cos(angle)) * increment * increment / 1000000 + yvelocity * increment / 1000; xcoord += xchange; ycoord += ychange; lander = AffineTransform.getTranslateInstance(xchange, ychange).createTransformedShape(lander); flames = AffineTransform.getTranslateInstance(xchange, ychange).createTransformedShape(flames); lander = AffineTransform.getRotateInstance(angleChange, xcoord, ycoord).createTransformedShape(lander); flames = AffineTransform.getRotateInstance(angleChange, xcoord, ycoord).createTransformedShape(flames); if (xcoord > width) { xcoord -= width; lander = AffineTransform.getTranslateInstance(-width, 0).createTransformedShape(lander); flames = AffineTransform.getTranslateInstance(-width, 0).createTransformedShape(flames); terrain = new LunarTerrain(width, height, terrain.getLastPoint()); terrainsPassed++; } else if (xcoord < 0) { xvelocity *= -0.75; lander = AffineTransform.getTranslateInstance(-xcoord, 0).createTransformedShape(lander); flames = AffineTransform.getTranslateInstance(-xcoord, 0).createTransformedShape(flames); xcoord = 0; } } private void computeVelocity(int increment) { xvelocity += (thrustOn * thrust * Math.sin(angle)) * increment / 1000; yvelocity += (gravity * gravityOn - thrustOn * thrust * Math.cos(angle)) * increment / 1000; } private void didLanderTouchdown() { if (terrain.intersects(lander.getBounds())) { gameRunning = false; didLanderLand(); landerTouchdown = true; thrustOn = 0; gravityOn = 0; if (!landerLanded) { remainingLives--; if (remainingLives < 0) { endGame = true; } } } else { gravityOn = 1; landerTouchdown = false; landerLanded = false; } } private void didLanderLand() { if (landingScore > 0 && terrain.getLandingArea().intersects(lander.getBounds())) { if (landerTouchdown) { gameRunning = true; } else { if (landingScore > 1600) { landingScore = (int) (Math.pow((landingScore - 1600), 0.5) * landingScore); } score += landingScore * terrainsPassed * terrainsPassed; } landerLanded = true; } else { landerLanded = false; } } private int getLandingScore() { return (int) (500.0 * (1 - (Math.abs(angle) / .2)) + 500.0 * (1 - (Math.abs(xvelocity) / 100.0)) + 1000.0 * (1 - (Math.abs(yvelocity) / 350.0))); } private Color getLandingColor() { int r, g, b; if (landingScore <= 0) { r = 100; g = 100; b = 100; } else if (landingScore <= 800) { r = 255; g = (int) (255 * landingScore / 800.0); b = 0; } else if (landingScore <= 1600){ r = 255 - (int) (255 * (landingScore - 800) / 800.0); g = 255 - (int) (100 * (landingScore - 800) / 800.0); b = 0; } else { r = 0; g = 155; b = 0; } return new Color(r, g, b); } public int getScore() { if (endGame) { return score; } return 0; } public Image getImage() { if (continueDrawing || gameRunning) { if (lastTerrain != terrainsPassed || lastTerrain == 0) { lastTerrain = terrainsPassed; transformedTerrain = scaleTransform.createTransformedShape(terrain); Line2D landingArea = terrain.getLandingArea(); landingLabelX =(int) ((landingArea.getX1() + landingArea.getX2()) / 2 * windowWidth / width) - 5; landingLabelY =(int) (landingArea.getY1() * windowHeight / height) + 16; } graphics2D.setPaint(skyColor); graphics2D.fill(new Rectangle(windowWidth, windowHeight)); graphics2D.setPaint(groundColor); graphics2D.fill(transformedTerrain); graphics2D.setStroke(line3px); graphics2D.setPaint(groundLineColor); graphics2D.draw(transformedTerrain); graphics2D.setPaint(textColor); graphics2D.setStroke(line2px); graphics2D.setFont(smallFont); graphics2D.drawString("Score:", marginX, marginY); graphics2D.drawString("Lives Left:", marginX, marginY + 40); graphics2D.drawString("Fuel Remaining:", marginX, marginY + 80); graphics2D.drawString("X:" + (int) xvelocity, marginX + 43, marginY + 120); graphics2D.drawString("Y:" + (int) yvelocity, marginX + 43, marginY + 135); graphics2D.drawString("Angle:" + (int) (angle * 180 / Math.PI), marginX + 43, marginY + 150); if (landerLanded && !gameRunning) { graphics2D.drawString("Landing Score:", marginX, marginY + 168); } graphics2D.setFont(largeFont); if (landerLanded && !gameRunning) { graphics2D.drawString("" + landingScore + "x" + (terrainsPassed * terrainsPassed) + " = " + (landingScore * terrainsPassed * terrainsPassed), marginX + 1, marginY + 191); } graphics2D.drawString("" + score, marginX + 1, marginY + 23); if (remainingLives > 2) { graphics2D.fill(life1); graphics2D.fill(life2); graphics2D.fill(life3); } else if (remainingLives > 1) { graphics2D.fill(life1); graphics2D.fill(life2); } else if (remainingLives > 0) { graphics2D.fill(life1); } else if (remainingLives < 0) { graphics2D.drawString("GAME OVER", windowWidth / 2 - 85, 5 * windowHeight / 12); } graphics2D.fill(new Rectangle(marginX + 1, marginY + 85, (int) ((float) windowWidth / 7 * remainingFuel / initialFuel), 18)); graphics2D.draw(new Rectangle(marginX + 1, marginY + 85, (int) ((float) windowWidth / 7), 18)); graphics2D.setStroke(line3px); graphics2D.draw(new Ellipse2D.Double(marginX, marginY + 112, 38, 38)); graphics2D.draw(new Line2D.Double(23.0 * Math.sin(angle) + marginX + 19, -23.0 * Math.cos(angle) + marginY + 131, 14.0 * Math.sin(angle) + marginX + 19, -14.0 * Math.cos(angle) + marginY + 131)); double conv; if (xvelocity == 0 && yvelocity == 0) { conv = 0; } else { conv = Math.pow(xvelocity * xvelocity + yvelocity * yvelocity, -0.5); } graphics2D.setStroke(line2px); graphics2D.draw(new Line2D.Double(22.0 * xvelocity * conv + marginX + 19, 22.0 * yvelocity * conv + marginY + 131, 15.0 * xvelocity * conv + marginX + 19, 15.0 * yvelocity * conv + marginY + 131)); graphics2D.setPaint(getLandingColor()); graphics2D.fill(new Ellipse2D.Double(marginX + 8, marginY + 120, 22, 22)); graphics2D.setPaint(skyColor); graphics2D.setFont(landingFont); if (terrainsPassed != 0) { graphics2D.drawString("" + terrainsPassed, landingLabelX, landingLabelY); } if (landerTouchdown && !landerLanded) { graphics2D.setPaint(explosionColor1); graphics2D.fill(scaleTransform.createTransformedShape(AffineTransform.getTranslateInstance(xcoord, ycoord).createTransformedShape(AffineTransform.getScaleInstance(1.00, 1.00).createTransformedShape(LunarLanderShapes.explosion())))); graphics2D.setPaint(explosionColor2); graphics2D.fill(scaleTransform.createTransformedShape(AffineTransform.getTranslateInstance(xcoord, ycoord).createTransformedShape(AffineTransform.getScaleInstance(0.75, 0.75).createTransformedShape(LunarLanderShapes.explosion())))); graphics2D.setPaint(explosionColor3); graphics2D.fill(scaleTransform.createTransformedShape(AffineTransform.getTranslateInstance(xcoord, ycoord).createTransformedShape(AffineTransform.getScaleInstance(0.50, 0.50).createTransformedShape(LunarLanderShapes.explosion())))); } else { if (thrustOn == 1 && gameRunning) { graphics2D.setPaint(flamesColor); graphics2D.fill(scaleTransform.createTransformedShape(flames)); } graphics2D.setPaint(landerColor); graphics2D.fill(scaleTransform.createTransformedShape(lander)); } continueDrawing = gameRunning; return image; } return image; } } class LunarLanderWindow extends Frame implements KeyListener, MouseListener, WindowListener { // -------------------------------------------------------------------------------- // Data Members // -------------------------------------------------------------------------------- private LunarLanderGame game; private Insets insets; private int windowWidth = 800, windowHeight = 600, increment = 100, maxIncrement = 100, gameWidth, gameHeight; private boolean loop = true; private Graphics2D graphics2D; private Date startTime, endTime; // -------------------------------------------------------------------------------- // Constructor // -------------------------------------------------------------------------------- public LunarLanderWindow() { // call parent constructor super("Lunar Lander"); // set frame properties insets = getInsets(); gameWidth = windowWidth - insets.left - insets.right; gameHeight = windowHeight - insets.top - insets.bottom; setSize(windowWidth, windowHeight); setResizable(false); Toolkit toolkit = Toolkit.getDefaultToolkit(); int xLocation = (toolkit.getScreenSize().width - windowWidth) / 2; int yLocation = (toolkit.getScreenSize().height - windowHeight) / 2; setLocation(xLocation,yLocation); setBackground(Color.black); show(); graphics2D = (Graphics2D) getGraphics(); validate(); reset(); // regester this frame as its own key and window listener addWindowListener(this); addKeyListener(this); // start infinate loop startTime = new Date(); while (loop) { run(); } } // -------------------------------------------------------------------------------- // Methods // -------------------------------------------------------------------------------- public void run() { game.advanceTime(increment); graphics2D.drawImage(game.getImage(), insets.left, insets.top, this); endTime = new Date(); System.out.println(increment + " " + (int) (endTime.getTime() - startTime.getTime())); increment = (int) (increment + endTime.getTime() - startTime.getTime()) / 2; if (increment > maxIncrement) { increment = maxIncrement; } startTime.setTime(endTime.getTime()); } private void reset() { game = new LunarLanderGame(gameWidth, gameHeight, createImage(gameWidth, gameHeight)); } // implementation of mouse listener public void mouseClicked(MouseEvent event) {} public void mouseEntered(MouseEvent event) {} public void mouseExited(MouseEvent event) {} public void mousePressed(MouseEvent event) {} public void mouseReleased(MouseEvent event) {} // implementation of key listener public void keyTyped(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyCode() == KeyEvent.VK_UP) { game.thrusterOff(); } else if (event.getKeyCode() == KeyEvent.VK_LEFT || event.getKeyCode() == KeyEvent.VK_RIGHT || event.getKeyCode() == KeyEvent.VK_DOWN) { game.stopLanderTurn(); } else if (event.getKeyCode() == KeyEvent.VK_SPACE || event.getKeyCode() == KeyEvent.VK_F3) { game.continueGame(); } else if (event.getKeyCode() == KeyEvent.VK_P) { game.pauseGame(); } else if (event.getKeyCode() == KeyEvent.VK_F2) { reset(); } } public void keyPressed(KeyEvent event) { if (event.getKeyCode() == KeyEvent.VK_UP) { game.thrusterOn(); } else if (event.getKeyCode() == KeyEvent.VK_LEFT) { game.turnLanderLeft(); } else if (event.getKeyCode() == KeyEvent.VK_RIGHT) { game.turnLanderRight(); } else if (event.getKeyCode() == KeyEvent.VK_DOWN) { game.resetLanderTurn(); } } // implementation of window listener to activate close button public void windowClosing(WindowEvent event) { loop = false; dispose(); System.exit(0); } public void windowActivated(WindowEvent event) {} public void windowClosed(WindowEvent event) {} public void windowDeactivated(WindowEvent event) {} public void windowDeiconified(WindowEvent event) {} public void windowIconified(WindowEvent event) {} public void windowOpened(WindowEvent event) {} }