import java.applet.Applet; import java.awt.*; import java.net.URL; import java.util.Random; import java.awt.event.*; //I implement a whole bunch of unneeded interfaces because I may want to //use them in the future. public class ShadowPuppets extends Applet implements Runnable, KeyListener, MouseListener, MouseMotionListener { //The animation frames of the birds flapping. private final String [] FrameNames = {"images/bird21.GIF", "images/bird22.GIF", "images/bird23.GIF"}; //Bird go boom. private final String splatimg = "images/death.GIF"; //The maximum update rate. This is a clock tick in the program. private final int EPOCH = 100; //Bird dimensions; Probably should get this from one of the images //computationally, but this works. private final int BIRDWIDTH = 78; private final int BIRDHEIGHT = 60; // All the images we may show Image [] birdframes = new Image[FrameNames.length]; private Image splat; // A place to show them private Screen wall; // The starting elevation for the ground. private int ground = 400; // Necessary utility objects private Thread animator; private Random r = new Random(); // Bird is a bidirectional linked list. head is a dummy element to start. // currentBird and mousecurrent are indices into the list so that the // thread and mousehandlers can work separately on the list. Bird head = new Bird(0 - BIRDWIDTH, 0 - BIRDHEIGHT, 0, 0); Bird currentBird = null; Bird mousecurrent = null; public void init() { p("Running ShadowPupptes..."); URL cb = getCodeBase(); // Load all the images. Use MediaTracker to make sure they load fully. MediaTracker mt = new MediaTracker(this); for (int i = 0; i < FrameNames.length; i++) { p("Loading image: "+cb+FrameNames[i]+"."); birdframes[i] = getImage(cb, FrameNames[i]); mt.addImage(birdframes[i], i); } splat = getImage(cb, splatimg); mt.addImage(splat, 101); try{ mt.waitForAll(); } catch (Exception e) { p(e); } // Layout the main window. setBackground(Color.white); setForeground(Color.black); setLayout(new GridLayout(1,1)); wall = new Screen(this, this); this.add(wall); repaint(); animator = new Thread(this, "zap"); } public void start() { p("It's alive!"); animator.start(); } public void run() { // Wait until the wall has initialized before we try to paint to it. while (wall.r == null) { try { Thread.sleep(100); } catch (Exception ex) { p(ex); } } p("Animator running"); //always animate the graphics while (true) { // For each cycle, blank the screen and draw the ground line. wall.blank(); wall.drawHoriz(ground+BIRDHEIGHT/2); while (currentBird != null) { // For each bird in each cycle, make it move/accelerate. currentBird.action(); // Eliminate all birds that are out of our viewing space Rectangle bounds = wall.r; if ((currentBird.getX() > bounds.width) || (currentBird.getX() < 0-BIRDWIDTH) || (currentBird.getY() > bounds.height)) { // p("destroying bird"); currentBird.destroy(); } else { // Otherwise draw them. // p("showing image "+currentBird.curframe+" at "+currentBird.getX()+ //", "+currentBird.getY()); // If they're alive, use a "living" frame. if (currentBird.alive) wall.showImage(birdframes[currentBird.curframe], currentBird.getX(), currentBird.getY()); else { // If they're dead, show the "splat." if (currentBird.curframe == 0) wall.showImage(splat, currentBird.getX(), currentBird.getY()); else // Remove any bird that's been dead long enough. currentBird.destroy(); } } currentBird = currentBird.next; } // Once all the birds have been updated, repaint. repaint(); wall.repaint(); // If the linked list is empty, freeze the thread so // we don't waste time updating nothing. if (head.next == null) { p("No living birds. Stopping thread to save cycles."); animator.suspend(); } // Steely Dan Postulate: Go back, Jack and do it again. currentBird = head.next; try { Thread.sleep(EPOCH); } catch (Exception x) { p(x); } } } public void stop() { animator.stop(); } public void mouseClicked(MouseEvent e) { // Figure out which button is down and offset the click so // it appears to be centered with the bird (as opposed to in // the ULH corner). int mod = e.getModifiers(); e.translatePoint(-BIRDWIDTH/2, -BIRDHEIGHT/2); // If button 1 is clicked, create a bird at that location. if ((mod & e.BUTTON1_MASK) != 0) { // p("I'm creating a bird at "+e.getX()+", "+e.getY()+"."); head.insertAfter(new Bird(e.getX(), e.getY(), ground,birdframes.length)); // Try to resume the thread, in case it was frozen. animator.resume(); } // If one of the other buttons was pushed... else if ((mod & (e.BUTTON2_MASK | e.BUTTON3_MASK)) != 0) { if (e.isShiftDown()) { // SHIFT CLICK set a new groundlevel int dground = e.getY() - ground; ground = e.getY(); // Inform each of the birds. mousecurrent = head.next; while (mousecurrent != null) { mousecurrent.groundlevel = ground; // If a bird is on the ground when it is moved, startle it. if ((mousecurrent.y < ground) && (mousecurrent.dy == 0)) { mousecurrent.dy = (r.nextDouble() * r.nextDouble() + .5) * 3; mousecurrent.dx = (r.nextDouble() * r.nextDouble() - .5) * 10; if (r.nextInt() %2 == 1) mousecurrent.dx *= -1; } if (mousecurrent.y > ground) { mousecurrent.dy -= (r.nextDouble() * r.nextDouble() + .5) * 8; mousecurrent.dx = (r.nextDouble() * r.nextDouble() - .5) * 30; if (r.nextInt() %2 == 1) mousecurrent.dx *= -1; } mousecurrent = mousecurrent.next; } } // !!!!!!!!!!!!!!!!!!! // !!!!PSYCHO MODE!!!! // !!!!!!!!!!!!!!!!!!! // Control-click shoots at the birds. else if (e.isControlDown()) { mousecurrent = head.next; int scareX = e.getX(); int scareY = e.getY(); while (mousecurrent != null) { mousecurrent.shoot(scareX, scareY, e.isAltDown()); mousecurrent = mousecurrent.next; } } // Without any modifiers, M2&M3 just startle the birds. else { mousecurrent = head.next; int scareX = e.getX(); int scareY = e.getY(); while (mousecurrent != null) { mousecurrent.scare(scareX, scareY); mousecurrent = mousecurrent.next; } } } } public void mouseDragged(MouseEvent e) { // p("DRAG: "+e.getX()+","+e.getY()+"."); mouseClicked(e); } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseMoved(MouseEvent e) { // p("MOVED: "+e.getX()+","+e.getY()+"."); } public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { // if (e.getKeyCode() == e.VK_LEFT) // walker.dx = walker.dx - 3; //if (e.getKeyCode() == e.VK_RIGHT) // walker.dx = walker.dx + 3; //walker.dy = 0; // System.out.println(e.getKeyCode()); //return true; //} } private void p(String s) { System.out.println(s); } private void p(Exception e) { System.out.println(e); } }