// 'pez.java', 6/01 Joe Rosen // 7/01/01, "PEZ", aka Rachel Fishman import java.applet.Applet; import java.awt.*; import java.net.URL; // classes // food object (e.g. pasta pocket, wheat kernal, pez candy, etc.) class FoodObject { // constants // food object images public static final int TOTAL_FOODOBJ_IMGS = 2; // total # of food object images // width and height public static final int FOODOBJ_W = 42; public static final int FOODOBJ_H = 42; // pez candy pt locs (left and right hand origins), offset from canvas top, left static final Point PEZ_PT_L = new Point(26,135); static final Point PEZ_PT_R = new Point(204,137); // anim public static final int PEZSPIN_FPS = 75; // pez spin anim delay // vars // flag active= true/inactive= false public boolean active_flg; // pez enlarge public boolean enlarge_flg; // true= pez enlarging public int enlarge_w, enlarge_h; // pez rectangle and position public Rectangle pez_rct; // fully expanded rect // float public boolean float_flg; // after growing/expanding begin floating public int cur_x, cur_y; // current position x,y public float move_x, offset_x; // x offset trajectory // pez spin frame public int pez_frame; // 7 frames, 0-6 (each 42 pixels vertically apart) // pez spin anim timer public long pezspin_anmdelay; // independent timing control over pez's spin anim rate // pez candy emerging from left or right hand? public int left_right; // 0= left, 1= right // static vars // drawing environment info (passed in from main class) public static Applet the_applet; public static int canvas_w, canvas_h; public static Component main_component; public static Graphics composite_graphics; // drawing canvas public static Rectangle canvas_rct; // images and media tracker public static Image[] foodobj_imgs = new Image[TOTAL_FOODOBJ_IMGS]; public static MediaTracker foodobj_img_tracker; // methods: // constructor // public static: // init_foodobj_environvars // load_foodobj_imgs // public: // activate_foodobj // draw_foodobject_pez // reset_foodobj // constructor void FoodObject() { // flag active= true/inactive= false active_flg = false; } // end 'constructor' // init static food obj applet environment vars public static void init_foodobj_environvars( Applet the_aplt, int can_width, int can_height, Graphics comp_graphics ) { // applet environment info the_applet = the_aplt; canvas_w = can_width; canvas_h = can_height; composite_graphics = comp_graphics; // drawing canvas canvas_rct = new Rectangle(0, 0, canvas_w, canvas_h); } // end method 'init_foodobj_environvars' // load food object images public static void load_foodobj_imgs() { // food object images and media tracker foodobj_img_tracker = new MediaTracker(the_applet); foodobj_imgs[0] = the_applet.getImage(the_applet.getCodeBase(), "images/pez_pez.gif"); foodobj_img_tracker.addImage(foodobj_imgs[0], 1); foodobj_imgs[1] = the_applet.getImage(the_applet.getCodeBase(), "images/pez_pez_1.gif"); foodobj_img_tracker.addImage(foodobj_imgs[1], 2); } // end 'load_foodobj_imgs' // activate (re-init) food object public void activate_foodobj(int l_r) { Point tmp_pt; // pez candy emerging from left or right hand? left_right = l_r; // left or right? (0= left; 1= right) if (left_right == 0) tmp_pt = new Point(PEZ_PT_L.x, PEZ_PT_L.y); else tmp_pt = new Point(PEZ_PT_R.x, PEZ_PT_R.y); // init pez enlarge enlarge_w = 4; enlarge_h = 4; enlarge_flg = true; // food object rect (max size rect, positioned before float) int tmp_x = tmp_pt.x - ((int) (FOODOBJ_W / 2)); int tmp_y = tmp_pt.y - ((int) (FOODOBJ_H / 2)); pez_rct = new Rectangle(tmp_x, tmp_y, FOODOBJ_W, FOODOBJ_H); // init current pez candy position x,y cur_x = tmp_pt.x - 2; cur_y = tmp_pt.y - 2; // float float_flg = false; // after expanding, begin floating // pez spin frame pez_frame = 0; // 7 frames, 0-6 (each 42 pixels vertically apart) // pez spin anim timer pezspin_anmdelay = System.currentTimeMillis() + PEZSPIN_FPS; // anim, x# fps; // flag active= true/inactive= false active_flg = true; } // end method 'activate_foodobj' // draw food object, pez candy public void draw_foodobject_pez() { Graphics clip_g; // clip/crop // expand (?) if (enlarge_flg == true) { // done enlarging ? if ( (enlarge_w >= pez_rct.width) || (enlarge_h >= pez_rct.height) ) { cur_x = pez_rct.x; cur_y = pez_rct.y; composite_graphics.drawImage(foodobj_imgs[1], cur_x, cur_y, FOODOBJ_W, FOODOBJ_H, null); enlarge_flg = false; // flag, pez is done expanding float_flg = true; // after growing/expanding begin floating // randomly choose an x coord offset trajectory // left if (left_right == 0) { offset_x = (Math.round(Math.random() * ((int) (canvas_w / 2))) - PEZ_PT_L.x) / ((float) PEZ_PT_L.y); move_x = (float) pez_rct.x; // accumulate rect x coord as a float (calculated from offset) //System.out.println("left, offset_x: " + offset_x); } // right else { offset_x = (Math.round(Math.random() * ((int) (canvas_w / 2))) - (PEZ_PT_R.x - (canvas_w / 2))) / ((float) PEZ_PT_R.y); move_x = (float) pez_rct.x; //System.out.println("right, offset_x: " + offset_x); } } // continue enlarging else { Point tmp_pt; // left or right? (0= left; 1= right) if (left_right == 0) tmp_pt = new Point(PEZ_PT_L.x, PEZ_PT_L.y); else tmp_pt = new Point(PEZ_PT_R.x, PEZ_PT_R.y); composite_graphics.drawImage(foodobj_imgs[1], cur_x, cur_y, enlarge_w, enlarge_h, null); enlarge_w++; enlarge_h++; cur_x = (int) (tmp_pt.x - (enlarge_w / 2)); cur_y = (int) (tmp_pt.y - (enlarge_h / 2)); } } // when pez candy is fully expanded, float/spin else if (float_flg == true) { // update spin frame? if (System.currentTimeMillis() >= pezspin_anmdelay) { // update pez spin frame if (pez_frame < 6) pez_frame++; // 7 frames, 0-6 (each 42 pixels vertically apart) else pez_frame = 0; // update pez spin anim timer pezspin_anmdelay = System.currentTimeMillis() + PEZSPIN_FPS; // anim, x# fps; } // offset move_x = move_x + offset_x; // accumulate rect x coord as a float (calculated from offset) pez_rct.move(Math.round(move_x), (pez_rct.y - 1)); // check to make sure pez candy is still within canvas if ( ! pez_rct.intersects(canvas_rct) ) reset_foodobj(); // shut down food obj else { clip_g = composite_graphics.create(); // create clip rect clip_g.clipRect(pez_rct.x, pez_rct.y, pez_rct.width, pez_rct.height); // draw pez candy into buffer clip_g.drawImage(foodobj_imgs[0], pez_rct.x, (pez_rct.y - (pez_frame * FOODOBJ_H)), the_applet); clip_g.dispose(); // dispose clip rect } } } // end method 'draw_foodobject_pez()' // reset food object public void reset_foodobj() { // shut down all flags enlarge_flg = false; float_flg = false; active_flg = false; } // end 'reset_foodobj' } // end class 'FoodObject' // main program class, "pez" public class pez extends Applet implements Runnable { // constants // x/y, drawings canvas static final int CANVAS_W = 238; static final int CANVAS_H = 344; static final int TOTAL_IMGS = 2; // total # of images // base buff idx references for image parts static final int BASE_IDX_RACHEL = 0; // rachels (frames 1 and 2, arms up and down) // image dimensions // rachel h x w static final int RACHEL_W = 208; static final int RACHEL_W_5_L = 107; // 1/2, left static final int RACHEL_W_5_R = 101; // 1/2, right static final int RACHEL_H = 325; // rachel loc, offset into canvas static final int RACHEL_X = 14; static final int RACHEL_Y = 15; // rachels, x coord idx into rachel strip static final int RACHEL_DOWN_1_X = 0; // arms down, squiggle frame 1 static final int RACHEL_DOWN_2_X = 418; // arms down, squiggle frame 2 static final int RACHEL_UP_X_ADD = 209; // add 209 pixels for arms up // total number of food objects (max#, and max# active at one time) public static final int TOTAL_FOODOBJS = 250; // vars // composite buffer, images and media tracker static Image composite_image; static Graphics composite_graphics; static Image[] imgs = new Image[TOTAL_IMGS]; static MediaTracker img_tracker; // graphics component of this applet, used to force immediate repaints static Graphics applet_graphics; // animation thread static Thread anim_thread; // squiggle anim timer static long squiggle_delay; // squiggle flag static int squiggle_flg; // 0-1, 2 frame squiggle anim // rachel, action flag static int rachel_action_flg; // 0= arms down; 1= left up; 2 = both up; 3 = right up // rachel, modesty flag static int rachel_modesty_flg; // 0= modest shorts; 1= original shorts // rachel, arms action (mouse down in) rectangle array: left | both | right static Rectangle action_rcts[] = new Rectangle[3]; // food objects array static FoodObject[] food_obj; // init flag static boolean init_flg = false; // false until initialization in 'main()' is complete // methods: // init // start // stop // destroy // update // run // mouseDown // mouseUp // keyDown // paint // private methods: // activate_pez_candy // my_paint // draw_rachel // 'init()' public void init() { int idx; // array index // load images and media tracker img_tracker = new MediaTracker(this); // rachel imgs[0] = getImage(getCodeBase(), "images/pez_rachel.gif"); img_tracker.addImage(imgs[0], 1); imgs[1] = getImage(getCodeBase(), "images/pez_rachel_m.gif"); img_tracker.addImage(imgs[1], 2); // composite buffer composite_image = createImage(CANVAS_W, CANVAS_H); composite_graphics = composite_image.getGraphics(); // graphics component of this applet, used to force immediate repaints applet_graphics = this.getGraphics(); // misc init // anim flags squiggle_flg = 0; // squiggle flag: 0/1, 2 frame squiggle anim= // anim timers squiggle_delay = System.currentTimeMillis() + 100; // squiggle anim, 10 fps // rachel, action flag rachel_action_flg = 0; // 0= arms down; 1= left up; 2 = both up; 3 = right up // rachel, modesty flag rachel_modesty_flg = 1; // 1= modest shorts; 0= original shorts // rachel, action rectangle array action_rcts[0] = new Rectangle(0, 0, (CANVAS_W / 3), CANVAS_H); // left action_rcts[1] = new Rectangle((CANVAS_W / 3), 0, (CANVAS_W / 3), CANVAS_H); // both action_rcts[2] = new Rectangle(((CANVAS_W / 3) * 2), 0, (CANVAS_W / 3), CANVAS_H); // right // init static food obj environment vars FoodObject.init_foodobj_environvars(this, CANVAS_W, CANVAS_H, composite_graphics); // instantiate food objects food_obj = new FoodObject[TOTAL_FOODOBJS]; for (idx = 0; idx < TOTAL_FOODOBJS; idx++) { food_obj[idx] = new FoodObject(); } // load food object images FoodObject.load_foodobj_imgs(); // init flag init_flg = true; // false until initialization is complete } // end method 'init()' // 'start()' // called when the applet becomes visible on screen public void start() { // create animation thread anim_thread = new Thread(this); // start animation thread anim_thread.start(); } // end method 'start()' // 'stop()' // called when the applet is no longer visible on screen public void stop() { // stop and destroy animation thread if (anim_thread != null) anim_thread = null; } // end method 'stop()' // 'destroy()' // called just before the browser exits public void destroy() { // stop and destroy animation thread if (anim_thread != null) anim_thread = null; // dispose applet graphics and offscreen graphics context/ pez composite buffer applet_graphics.dispose(); applet_graphics = null; composite_graphics.dispose(); composite_graphics = null; } // end method 'destroy()' // 'update()' // update the screen public void update(Graphics g) { // intercepting 'update()' avoids background erase if (composite_graphics != null) paint(g); // call 'paint()' } // end method 'update()' // 'run()', thread runner public void run() { int idx; int paint_flg = 0; while ( anim_thread == Thread.currentThread() ) { // anim timers // squiggle if (System.currentTimeMillis() >= squiggle_delay) { // squiggle flag if (squiggle_flg == 1) squiggle_flg = 0; // 0/1, 2 frame squiggle anim else squiggle_flg = 1; squiggle_delay = System.currentTimeMillis() + 100; // squiggle anim, 10 fps // flag paint paint_flg = 1; } // activate another pez candy ? if (rachel_action_flg > 0) { boolean tmp_flg = false; // are there any pez candy's still enlarging? for (idx = 0; idx < TOTAL_FOODOBJS; idx++) { if (food_obj[idx].enlarge_flg == true) { tmp_flg = true; break; // we found an enlarging pez candy, we're done here } } // only activate after we've determined mouse down and no other enlarging candy if (tmp_flg == false) { activate_pez_candy(); // activate pez candy paint_flg = 1; // flag paint, we've activated pez candy } } // paint (??) if (paint_flg == 1) repaint(); // force repaint // sleep try { Thread.sleep(25); } catch (InterruptedException e) {} } // end "while (Thread.currentThread() == anim_thread)" } // end method 'run()' // 'mouseDown()' // called when the mouse button is pressed down public boolean mouseDown(Event evt, int x, int y) { int idx; if ( !init_flg ) return true; // return immediately if we're not done initializing // which section of canvas was clicked? for (idx = 0; idx <= 2; idx++) { if ( action_rcts[idx].inside(x,y) ) { // rachel action flag, arms up rachel_action_flg = idx + 1; // 1= left, 2= both, 3= right //System.out.println("in rect: " + (idx + 1)); } } activate_pez_candy(); // immediately activate pez candy // immediately force repaint (draw arms up) my_paint(applet_graphics); return true; } // end method 'mouseDown()' // 'mouseUp()' // called when the mouse button comes back up public boolean mouseUp(Event evt, int x, int y) { if ( !init_flg ) return true; // return immediately if we're not done initializing // rachel action flag, arms down rachel_action_flg = 0; // immediately force repaint (draw arms down) my_paint(applet_graphics); return true; } // end method 'mouseUp()' // 'keyDown()' public boolean keyDown(Event evt, int key) { int idx; // System.out.println("key: " + key); // on "m" or "M" key down toggle shorts modesty if ( (key == 77) || (key == 109) ) { // rachel, modesty flag: 0= modest shorts; 1= original shorts if (rachel_modesty_flg == 0) rachel_modesty_flg = 1; else rachel_modesty_flg = 0; // immediately force repaint (draw modesty change) my_paint(applet_graphics); } return true; } // end method 'keyDown()' // 'paint()' public void paint(Graphics g) { // if init's complete, draw images in composite buffer and blast to screen if (init_flg) my_paint(g); } // end method 'paint()' // private methods // activate pez candy void activate_pez_candy() { int idx, idx_r; // activate first inactive food object for (idx = 0; idx < TOTAL_FOODOBJS; idx++) { if (food_obj[idx].active_flg == false) { // left if (rachel_action_flg == 1) food_obj[idx].activate_foodobj(0); // ^ 0= left; 1= right // both else if (rachel_action_flg == 2) { food_obj[idx].activate_foodobj(0); // pick second food obj (right hand pez) for (idx_r = 0; idx_r < TOTAL_FOODOBJS; idx_r++) { if (food_obj[idx_r].active_flg == false) { food_obj[idx_r].activate_foodobj(1); break; } } } // right else if (rachel_action_flg == 3) food_obj[idx].activate_foodobj(1); break; // we've found an active food object, we're done here } } } // end method 'activate_pez_candy()' // draw images in composite buffer and blast to screen // synchronized method, don't initiate another "paint" session until one's complete synchronized void my_paint(Graphics g) { int idx; // if composite buffer's non-existent, return immediately if (composite_graphics == null) return; // fill composite buffer with white paint composite_graphics.setColor(Color.white); composite_graphics.fillRect(0, 0, CANVAS_W, CANVAS_H); // have images loaded? if ( (! img_tracker.checkAll(true)) || (! FoodObject.foodobj_img_tracker.checkAll(true)) ) { // text font Font txt_Font = new Font("Helvetica", Font.PLAIN, 9); // set text string font composite_graphics.setFont(txt_Font); // set text string color composite_graphics.setColor(Color.black); // write to screen composite_graphics.drawString("Loading Images...", 25, 25); } else { draw_rachel(); // draw rachel // draw all active food objects for (idx = 0; idx < TOTAL_FOODOBJS; idx++) if (food_obj[idx].active_flg == true) food_obj[idx].draw_foodobject_pez(); } // black border composite_graphics.setColor(Color.black); composite_graphics.drawRect(0, 0, (CANVAS_W - 1), (CANVAS_H - 1)); // blast composite buffer to screen g.drawImage(composite_image, 0, 0, null); } // end method 'my_paint()' // draw rachel void draw_rachel() { int idx, x_coord, clip_x, x_img_offset, tmp_width; Graphics clip_g = null; // clip/crop // modesty? if (rachel_modesty_flg == 1) idx = 1; else idx = 0; // draw rachel // squiggle if (squiggle_flg == 0) x_coord = RACHEL_DOWN_1_X; else x_coord = RACHEL_DOWN_2_X; // draw left, both or right arm(s) switch (rachel_action_flg) { // arm up left case 1: clip_x = RACHEL_X; x_img_offset = RACHEL_X - (x_coord + RACHEL_UP_X_ADD); tmp_width = RACHEL_W_5_L; // right arm down clip_g = composite_graphics.create(); // create clip rect clip_g.clipRect(RACHEL_W_5_L, RACHEL_Y, RACHEL_W_5_R, RACHEL_H); clip_g.drawImage(imgs[idx], (RACHEL_X - x_coord), RACHEL_Y, this); clip_g.dispose(); // dispose clip rect break; // both arms up case 2: clip_x = RACHEL_X; x_img_offset = RACHEL_X - (x_coord + RACHEL_UP_X_ADD); tmp_width = RACHEL_W; break; // arm up right case 3: clip_x = RACHEL_X + RACHEL_W_5_L; x_img_offset = RACHEL_X - (x_coord + RACHEL_UP_X_ADD); tmp_width = RACHEL_W_5_R; // left arm down clip_g = composite_graphics.create(); // create clip rect clip_g.clipRect(RACHEL_X, RACHEL_Y, RACHEL_W_5_L, RACHEL_H); clip_g.drawImage(imgs[idx], (RACHEL_X - x_coord), RACHEL_Y, this); clip_g.dispose(); // dispose clip rect break; // arms down case 0: default: clip_x = RACHEL_X; x_img_offset = RACHEL_X - x_coord; tmp_width = RACHEL_W; break; } clip_g = composite_graphics.create(); // create clip rect clip_g.clipRect(clip_x, RACHEL_Y, tmp_width, RACHEL_H); // draw rachel into buffer clip_g.drawImage(imgs[idx], x_img_offset, RACHEL_Y, this); clip_g.dispose(); // dispose clip rect } // end method 'draw_rachel()' } // end class "pez"