////  Monster Mash ////

String title=     "M O N S T E R   S M A S H";
String subtitle=  "   Dracula walks the Earth!!!";
String hint1="Click near hero, to move him away from danger!";
String hint2="         ( Press ? key for help.)";
String news="", oldnews="";
String author="Lorcan & bam  [version 3731m]";
int frameNumber=0;
float ran=1;

boolean bug=false;
boolean hide=false;
boolean freeze=false;       // 'F' freezes monsters
boolean cheat=false;        // 'C' protects the Hero from capture.
boolean zoneshow=false;     // 'Z' shows motion zones.

String B=" ";
String NL="\n";

int oldKeyCode=0, keyCodeCount=0;


int right=700;                // screen size
int bottom=500;
float horizon=  bottom * 1/3;
// House //
float wall=20, door=180;
float floor=horizon+10;
float roof=horizon-60;
float path=floor+20;

float far;
float trans=200, weight=20;

float moonX, moonY;
float weedX=10, weedY=10;


float helpX, helpY, helpDY=0;
int reminder=0;

int treasureCount=0;

Hero me;
Witch hazel;
Vampire dracula;
Monster frank;
Treasure gold;



void setup() {
  size(right, bottom);
  begin();
}
void begin() {
  ////  Move creatures back to starting positions.
  me= new Hero();
  hazel=  new Witch();
  dracula = new Vampire();
  frank  = new Monster();
  gold = new Treasure();
  gold.x=  width-random(200);
  gold.y=  random(horizon, height-50);
  //
  moonX=  50 + random( width-100 );
  moonY=  hazel.y + random( 50 ); 
  helpX=  door+20;
  helpY=  horizon+12;
  helpDY=0;
}

void draw() {
  //// Draw the frame.
  frameNumber++;      // Frame count
  scene();            // Draw scene.
  // Move & display (unless in help mode).
  helpX=  door+20;
  helpY=  horizon+12;
  helpDY=0;
  fill(255);
  if (key=='=') {
  }    // Pause.
  else if (key == '?') help();
  else if (key == '!') cheat();
  else {
    creatures();
    collisions();
  }
  //// Write messages last (so they always appear).
  messages();
  if (bug) debug();
}
void creatures() {
  //// Move and show all creatures.
  me.move();
  me.show();
  // Witch flies past moon.
  hazel.move();
  hazel.show();  
  //// Monsters
  dracula.move();
  frank.move();
  //
  dracula.show();
  frank.show();
}
void collisions() {
  //// Check for collisions ////
  if ( gold.near( dracula.x, dracula.y ) < 100 ) { gold.reset(); news="Count Dracula took the GOLD!"; }
  if ( gold.near( frank.x, frank.y ) < 100 ) { gold.reset(); news="The monster ate the treasure."; }
  if ( gold.near( me.x, me.y ) < 50) {
    me.score += 500;
    news=  "Hero got the treasure!!!!  ("+gold.points+" points)";
    treasureCount++;
    gold.reset();
    me.x=  door-100;                    // Move hero to house
    me.y=  floor;
  }
  if (abs(hazel.x-moonX) < 25  && hazel.dx<0) {
    //// Witch crossing the moon.
    background(100, 0, 0);    // Flash (dark red)
    fill(255, 255, 0);
    text( "HEH \n     HEH      \n       HEH", hazel.x+50, hazel.y-50 );
    if (abs(hazel.x-moonX) < 2 ) {
      me.score -= 100;
      news=  "The witch crossed the moon!  Hero loses points and treasure!";
      if (! hide) {
        treasureCount=  treasureCount/2;
        news +=  " (and half of treasure!)";
      }
      me.upset();
    }
  }// hazel/moon  //

  if ( me.lives<=0 ) {  news= "Click near hero, to move him." ; } 
  hide=  me.x<=door+20 && me.y<=floor;
  if (hide) {
    //// SAFE in the house!
    fill(0);
    text( "(Hiding.)", wall+20, roof );
    if (frameNumber % 30 == 0) me.score -= 1;        // Lose 1 point per second.
  } else if (cheat) { 
    text( "(CHEATING.)", door-80, roof );  
  } else {
    //// Check for attacks -- if NOT hiding (or cheating)
    if (me.near( frank.x, frank.y ) < frank.toofar) {
      //// Caught by Frank!
      background(0, 255, 0);
      me.score -= 100;
      me.reset();
      frank.reset();
      news=  "Hero was attacked  by Frankenstein!";
    }
    if (me.near( dracula.x, dracula.y ) < dracula.toofar) {
      //// Caught by Dracula!
      background(255,0,0);
      me.score -= 100;
      me.reset();
      dracula.reset();
      news=  "Hero was attacked by Dracula!";
    }
  }
    //// Dracula battles the Monster! ////
    if ( ! dracula.bat && dist( dracula.x, dracula.y, frank.x, frank.y ) < 200) {
      //// Frank bumps into Dracula!
      background(127);
      news=  "Frankenstein vs. Dracula.";
      frank.dx *= -1;
      frank.x += 10*frank.dx;
      fill(255, 127, 0);
      me.score += (int) random(2);
      int n= (int) (frameCount / 20) % 4;            // Flash for ten frames.
      if (n==0) frank.show();
      else if (n==2) dracula.show();
      else if (n==1) {
        background(255,0,255);
        fill(255, 0, 0); 
        textSize(18);
        text( "Dracula BITES the monster.", width/2, dracula.y-60 );
        news= "Dracula BITES the monster.";
      } 
      else if (n==3) {
        background(0,255,255);
        fill(0, 127, 255);
        textSize(20);
        text( "Frankenstein clobbers  Dracula.", width/2, frank.y+60 );
        news= "Frankenstein clobbers  Dracula.";
      } 
      else text( "B U G !", width/2, frank.y-80 );
    
  }
}


//// EVENT HANDLERS ////

void mousePressed() {
  //// Check for clicking on hero, witch eye, etc.
  far=  me.near( mouseX, mouseY+40 );      // Use center of hero (instead of y).
  if (far>me.tooclose && far<me.toofar) {
    float jump=  0.25 + random(0.25);    // jump towards mouse (25-50%); 
    me.x += (mouseX-me.x) * jump;
    if (me.y > floor  || me.x > door ) {       // vertical jumps only when out of the house! 
      me.y += (mouseY+40-me.y) * jump;
    }
    if (me.x < wall) me.x=  wall+50;
    if (me.y <= floor) me.y=  floor;
    if (me.x>door+20 && me.y <= path) me.y=  path;
  }
  ////// Witch's eye adds 250 to score (and ends the night).
  if ( dist( mouseX, mouseY, hazel.x-20, hazel.y-50 )  <  15 ) { 
    background(0);
    me.score += 250;           // Witch's eye
    hazel.dx +=  1;
    me.x += 100;              // Hero jumps past witch.
    news=  "CLICKED on the Witch.  (Gain 250 points.)";
    if (hazel.dx > 0) {
      me.score += 1000;           // Turnagain.
      news=  "Congratulations.  You turned the witch around!!!  (+100 points.)";
    }
  }
}


void keyPressed() {
  //// Keys.
  if (key == 'q') exit();
  if (key == 'D') bug = ! bug;
  if (key == 'F') freeze = ! freeze;              // Monsters freeze
  if (key == 'C') cheat = ! cheat;      // Protect the hero from capture. 
  if (key == 'H') { me.x=  door-40; me.y=  floor; }        // Hide immediately.
  if (key == 'L') { 
    me.lives++;
    me.reset();
  }
  if (key == 'M') frank.reset();
  if (key == 'N') news="";
  if (key == '-') frameRate( 1 + frameRate/2 );
  if (key == '+') frameRate( frameRate*2 );
  if (key == 'T') { 
    me.go( gold.x, gold.y );
  }
  if (key == 'U') dracula.undead();
  if (key == 'V') { 
    dracula.undead(); 
    dracula.x=frank.x; 
    dracula.y=frank.y;
  };
  if (key == 'W') { 
    hazel.x=width;
  };        // Witch immediately starts at right side.
  if (key == 'Z') zoneshow = ! zoneshow;        // Show protection zone.
  if (key==CODED) {
    if (keyCode==oldKeyCode) {
      keyCodeCount++;
    } else { keyCodeCount=0; }
    oldKeyCode=  keyCode;
    int k=  1+keyCodeCount;
    if (keyCode == SHIFT) { }
    else if (keyCode == UP) me.y -= k;
    else if (keyCode == DOWN) {
      if (!hide) me.y+= k;
    }
    else if (keyCode == LEFT) me.x-= k;
    else if (keyCode == RIGHT) me.x+= k;    
    else if (keyCode == TAB) me.x += 50;      //???
    else if (keyCode == ESC) me.reset();      //Does not work!  Exits.
    else {
      println( "Unrecognized key '"+key+"'   keyCode="+keyCode );
    }
  }
}


//// MESSAGGES ////
void help() {
  //// Display help msgs???
  fill(0, 0, 150);
  next("Get hero to walk by clicking near him (but not too close).");
  next("Get treasure for points, but avoid monsters!");
  next("Witch crosses moon and steals treasure!  (Click eye, to reverse direction.)");
  next("Hero is safe in house, but loses points.");
  next("  +/-  Faster frame rate.");
  next("  =  Pause.");
  next("  q  Quit.");
  next("  !");
  reminder=  (int) frameRate * 300;        // Don't show hint #1 for 5 minutes.
}
void cheat() {
  //// Display cheat codes (after help).
  help();
  fill(255, 0, 0);
  next("CHEAT CODES");
  fill(150, 0, 0);
  next("  D  Debug");
  next("  C  Cheat!  (No captures.)");
  next("  F  Freeze the monsters");
  next("  H  Hide immediately.");
  next("  L  Next life");
  next("  M  New monster (Frank).");
  next("  N  (Clear the 'news' text'.)");
  next("  T  Treasure");
  next("  U  Undead.  (Bat transforms into Vampire).");
  next("  V  Vampire fights monster.");
  next("  W  New witch (starts at right).");
  next("  Z  Display movement zones");
  next("  UP,DOWN,LEFT,RIGHT" );
}

void messages() {
  //// Messages on screen.
  //// Text on screen
  fill(0);
  textSize(32);
  text( title, 200, 40 );
  if (! dracula.bat) {
    textSize(24);
    fill(255, 255, 0);
    text( subtitle, 200, 70 );
  }
  textSize(12);
  fill(0, 0, 127);
  text( author, 20, height-24);
  fill(0, 127, 0);
  if (reminder < 1) text( hint1, width/2, height-36 );
  text( hint2, width/2, height-24 );
  // Score & help
  if (me.score != 0) {  
    fill(255);
    if (me.score<0) fill(255, 0, 0);    // Negative (red).
    textSize(12); 
    text( "SCORE :", width*3/4, horizon-50 );
    textSize(18);  
    text( me.score, width*3/4, horizon-30 );
  }
  if (treasureCount > 0) {  
    fill(255, 150, 0);
    textSize(12); 
    text( "Treasures :", 100+width*3/4, horizon-50 );
    textSize(18);  
    text( treasureCount, 100+width*3/4, horizon-30 );
  }
  zones();
  // News.
  fill(255);
  textSize(18);
  //--  text( news, 20 + width*1/4, horizon-10 );
  text( news, door+20, horizon-10 );
  if (! news.equals(oldnews)) println( news );
  oldnews=  news;
}
void next( String s) {
  //// Display next message.
  text( s, helpX, helpY+14*helpDY );
  helpDY++;        //// Drop to next line.
}
void debug() {
  //// Debugging messages (if "D" key is pressed)
  textSize(12);
  fill(255, 255, 0);
  next( hazel.name+":  "+(int)hazel.x+","+(int)hazel.y );
  next( dracula.name+":  "+(int)dracula.x+","+(int)dracula.y );
  next( frank.name+":  "+(int)frank.x+","+(int)frank.y );
  next( "chomp:  "+frank.chomp );
  next( me.name+":  "+(int)me.x+","+(int)me.y );
  next( horizon+":  "+horizon );
  next( horizon+":  "+horizon );
  next( "wall,door,roof,floor:  "+(int)wall +B+(int)door  +B+(int)roof  +B+(int)floor );
  if (key=='=') return;
}
void zones() {
    //// Make collision zones visible ////
    if (zoneshow || frameCount %90 == 0) {
      // Hero motion zones (click radius*2);
      me.zones();
      // Monster  motion zones (collision  radius).
      if (frank.x >= 0) {
        frank.zone();
      }
      // Vampire  motion zones (collision  radius).
      if (! dracula.bat) {
        dracula.zone();
      }
    }
}
void strokeWidth( float w ) { 
  strokeWeight(w);
}


//// SCENE ////
void scene() {
  //// Sky & grass;
  if (dracula.bat) background( 200, 220, 255 );
  else  background( 50, 100, 255 );
  // Moon
  fill( 255, 170, 150 );
  noStroke();
  ellipse( moonX, moonY, 70, 70 );
  fill(150,100,100, random(40) );
  ellipse( moonX-15, moonY-15, 12,12 );
  ellipse( moonX+15, moonY-15, 12,12 );
  ellipse( moonX, moonY, 10,10 );
  ellipse( moonX, moonY+15, 40,10 );
  //// field & grass
  fill( 100, 200, 100 );
  noStroke();
  rectMode(CORNER);
  rect(0, horizon, width, height-horizon );
  // House & treasure//
  house();
  if (key=='=' || freeze) { 
    return;
  }    // Return, if paused.
  gold.chest( treasureCount );
  gold.show();
  seaweed( bottom );
}
void house() {
  // House //
  fill(255, 0, 255);
  if (hide) fill( 255, 200, 255 );    // House lights on, while hiding.
  rectMode(CORNERS);
  rect( wall, roof, door, floor+40);
  triangle( wall-15, roof, door+25, roof, (wall+door)/2, roof-45 );
  fill(220, 0, 220);      // Door
  rect( door-55, roof+5, door-10, floor+35);
  fill(100,0,100);
  rect( wall, floor+35, door, floor+40);
  //// Path to gold.
  if (! freeze) {
    stroke(255, 255, 0, 150 );
    strokeWeight(20);
    line( door-20, path+40, gold.x, gold.y+40);
    strokeWeight(0);
  }
}
void seaweed( float y ) {
  //// Make seaweed across the width of the screen.
  if (key=='=') { 
    return;
  }    // Pause.
  if (frameCount %10 == 0) {
    weedX=  10 - random(0, 20);
    weedY=  20 - random(0, 4);
  }
  for (float x=0; x<width+30; x+=30 )
  {
    strokeWeight(2);
    stroke( 50, 100, 0 );          // Dark-green seaweed.
    line(x, y, x+weedX, y-weedY );
    noStroke();
    weedX = weedX -1 + random(2);
  }
}



//////// CLASS DEFINITIIONS ////////

class Treasure {
  //// Treasure on the grass //
  float x, y, w=90, h=60;
  int r=200, g=200, b=100;      // colors
  int points=250; 
  void reset() {
    x=  width-random(200);                     // Treasure on the grass.
    y=  random(horizon+20, height-20);
    r=  180 + (int)random(70);                 // Random gold colors.
    g=  100 + (int)random(120);
    b=  50 + (int)random(100); 
    points=  (int) random( 100, 500 );       // Treasure on the grass.
  }
  void show() {
    fill( r+random(2), g+random(2), b+random(2) );
    ellipse( x, y, w, h );
    fill( r-20+random(20), g-20+random(20), b-20+random(20) );
    ellipse( x, y, w*3/4, h*3/4 );
    fill( r+5-random(5), g+5-random(5), b+5-random(5) );
    ellipse( x, y, w*1/2, h*1/2 );
    fill( r+8+random(8), g+8+random(8), b+8+random(8) );
    ellipse( x, y, w*1/4, h*1/4 );
    textSize(12);
    fill( 150, 100, 0);
    text( points, x+30, y+30 );
  }
  void chest( int n) {
    // Treasure chest in the house. //
    for (int j=0; j<treasureCount; j++ ) {
      float xt=  wall+20 + random(6*treasureCount);
      float yt=  floor+20 - random(3*treasureCount);
      fill( 180+random(70), 100+random(70), 50+random(100) ); 
      ellipse( xt, yt, 8, 8 );
    }
  }
  float near( float xx, float yy ) {
    //// Is something near me?
    return dist( x, y, xx, yy );
  }
}

class Witch {
  //// Wiitch flies across sky; interacts with the moon.  
  // Click on witch to reverse direction!
  float x=200, y=75, dx= -2;
  float eyeX, eyeY;
  String name="Hazel";

  void move() {
    //// Witch move
    if (freeze) return;
    x += dx;
    if (x > 2000) dx=  -2; 
    if (x < 100) {               // New witch.
      x=  random(5*width);        // Random wait for next appearance.
      y=  random(50, 100);
      moonX=  random(width);
      moonY=  random(horizon);
      return;
    }
  }
  void show( ) {
    //// Draw witch
    fill(100, 50, 50);
    rectMode(CENTER);
    rect( x, y, 40, 60 );    // Brown rectangle.
    fill(255, 0, 0);                      // Red face
    ellipse( x, y-40, 20, 20);
    // Yellow eye
    fill(255, 255, 0);
    ellipse( x-5, y-43, 5, 5 );
    // Black hat.
    fill(0);
    triangle( x-10, y-50, x+10, y-50, x, y-70 );
    strokeWeight(3);
    stroke(0);
    line( x-20, y-50, x+20, y-50 );  // Hat brim.
    drawBroom( x-60, x+60, y+30 );
  }
  void drawBroom( float x1, float x2, float y ) {
    //// Broom from x1 to x2, at y
    strokeWeight(6);
    stroke(200, 150, 100);
    line( x1, y, x2, y );
    //// Bristles at x2.
    strokeWeight(1);
    float yy=  y-10;
    for (int j=0; j<8; j++ )
    {
      line( x2, yy, x2+40, yy );
      yy += 3;
    }
    noStroke();        // Reset
  }
}// class Witch //

class Vampire {
  //// Vampire flies fast across sky ( a few times), then walks (slowly, but down).
  float x=10, y=75, dx=1, dy=0.25;
  String name="Vlad";
  float  batspeed=5;
  boolean bat=true;
  float toofar=100;            // Distance from vampire, for capture.

  void move() {
    if (freeze) return;
    if (x<0) x= 20;
    x += dx + 1 - random(2);
    y += dy + 1 - random(2);
    if (bat) {
      x += random(3) + batspeed;
      y += -10 +random(20);
    }
    //// Vampire alwaysd wraps  at right side of screen.
    if (x<width-20) return;
    x=10;                              // Wrap.
    news="";
    if (y < horizon+50) {              // Bat is above the horizon.
      if (frameNumber % 3 > 0) reset();
      else undead();
    } 
    else if (y<height-40) {
      x=10;                        // Wrap (until bottom).
      bat=  false;
    } 
    else {
      bat=true;                    // Revert to bat shape.
      y=  random(10, horizon-50 );  
      batspeed=  random(5);
    }
  }
  void show() {
    if (bat) {
      fill(0);
      //--  rect(x,y, 40,20);
      fill(0);
      triangle( x-20, y, x-10, y-30, x, y);
      triangle( x+20, y, x+10, y-30, x, y);
      triangle( x-10, y+5, x-5, y-20, x+10, y+5);
    } 
    else {
      fill(0);
      triangle( x-50, y+60, x, y-50, x+50, y+60);    // Cloak
      triangle( x-50, y-50, x, y-10, x+50, y-50);    //Collars
      fill(255, 0, 255);
      triangle( x-20, y-40, x, y-10, x+20, y-40);    // Collar red lining
      // 
      fill(255);
      triangle( x-20, y+60, x, y-20, x+20, y+60);    // Shirt & name
      fill(255, 0, 255);
      text(name, x-12, y+50);
      head( x, y-55 );
    }
  }
  void head( float xh, float yh ) {
    // Head (using x,y from args
    fill(255);
    ellipse( xh, yh, 30, 40 );                  // Head 
    fill(0);
    // eyes (blink)
    int ew=6, eh=6, grin=0;
    if (frameCount/60 % 2 == 0) {
      ew=7; 
      eh=3;
      grin=2;
    }
    ellipse( xh-8, yh-10, ew, eh );
    ellipse( xh+8, yh-10, ew, eh );
    stroke(0);                                  // Mouth
    strokeWeight(4+grin);
    curvetest( xh-25, yh-20, xh-10, yh+5, xh+10, yh+5, xh+25, yh-20 );  
    strokeWeight(3);      
    fang(xh-8, yh+5);
    fang(xh+8, yh+5);
    strokeWeight(0);      
    fill(255);
  }
  void fang( float x, float y) {
    stroke(255, 0, 0);
    triangle( x-1, y, x+1, y, x, y+5 );
  }
  void curvetest( float x, float y, float  xx, float yy, float  xxx, float yyy, float xxxx, float yyyy ) 
  {
    curve( x, y, xx, yy, xxx, yyy, xxxx, yyyy );   
    if (key=='=') {
      text( "curve( "  +x+", "  +y+", "+ xx+", "+yy+", "+ xxx+", "+yyy+", "+xxxx+", "+yyyy, xxxx+50, yyyy );   
      stroke(255, 0, 0);
      point( x, y );
      point( xx, yy );
      point( xxx, yyy );
      point( xxxx, yyyy );
    }
  }

  void reset() {        //++++ Revert to bat shape.
    bat=true;
    x=  20;
    y=  random(10, horizon-50 );
    news=  "";
  }
  void undead() {
    // Dracula walks the Earth!!!
    y=  height/2 - 50 + random(100); 
    bat=  false;
    batspeed=0;
  }
  void zone() {    //// Show collision zone.
      fill( 0, 0, 255, 30 );    // transparent
      ellipse( dracula.x, dracula.y-40, dracula.toofar*2, dracula.toofar*2); 
      fill( 0, 0, 255 );
      ellipse( dracula.x, dracula.y, 4, 4);
  }
}// class Vampire //

class Monster {
  String name="Frank";
  float x=400, y=300, dx=-12, dy=15;
  float xleft=0, xright=0;  
  float chomp=0, skew=0;
  float toofar=150;            // Distance from monster, for capture.

  //// METHODS ////
  void move() {
    if (freeze) return;    
    if (frameCount % 15 == 0) {
      x += dx + random(6);
      y += dy - dy * random(2);
      if (x<20) {    // bounce off walls
        dx= +12;    // Move RIGHT
        x = 20;
        news="";        // Clear news when Frank bounces off sides.
      }
      if (x>width-20) {
        dx= -12;    // LEFT
        x = width-20;
        news="";        // Clear news when Frank bounces.
      }
      if (y<horizon+20) {    // bounce off bottom & top
        dy= +12;    // DOWN
        y= horizon+20;      // bounce away from limit.
      }
      if (y>height-150) {
        dy= -12;    // UP
        y=  height-150;
      }
      //  Wobble.TT
      if (dx>0) {
        xleft=  -5+random(2*dx);    // Moving RIGHT
        xright=  5-random(2*dx);
      }
      if (dx<0) {
        xleft=  -5-random(2*dx);
        xright=  5+random(2*dx);
      }
    }
  }
  void show() {
    rectMode(CENTER);
    fill(0, 175, 100);
    rect(x, y, 50, 80);         // Body
    fill(0, 150, 100);
    rect(x, y-70, 40, 40);      // Head
    rect(x, y-50, 20, 20);      // Neck
    fill(0, 100, 0);
    text(name, x-15, y+10);
    fill(255, 0, 0);
    // Eyes look in direction of motion .
    ellipse(x-8-xleft, y-80, 4, 4);
    ellipse(x+8-xleft, y-80, 4, 4);
    stroke(200, 50, 0);
    strokeWeight(3-2*chomp);
    if (frameCount % 30 == 0) {
      chomp=  1-random(2); 
      skew= 1-random(2);
    }    // Random from -1 to +1
    float mouth=  y-60;
    fill(220, 220, 50);
    rectMode(CENTER);
    rect( x+chomp, mouth+skew, 26+8*chomp, 10+4*skew );            // Mouth
    strokeWeight(1);
    line( x-10+chomp+skew, y-60+chomp-skew, x+10-chomp+skew, y-60-chomp-skew );
    noStroke();
    // Arms
    strokeWeight(12);
    stroke(0, 100, 50);
    line( x-20, y-30, x-18-3*xleft, y+40 );    // Left arm    
    line( x+20, y-30, x+18-3*xright, y+40 );    // Left arm    
    // Legs
    strokeWeight(12);
    stroke(0, 100, 50);
    line( x-20, y+45, x-18-xleft, y+95 );    // Left leg    
    line( x+20, y+45, x+18-xright, y+95 );    // Left leg    
    strokeWeight(0);
    fill( 100, 50, 0 );                        // Shoe color
    rect( x-24-xleft, y+95, 25, 15 ); 
    rect( x+24-xright, y+95, 25, 15 );
  }
  void reset() {
    x=  width-50;
    y=  random(horizon+50, height-50);
  }
  void zone() {    //// Show collision zone.
      fill( 0, 0, 255, 30 );    // transparent
      ellipse( frank.x, frank.y-40, frank.toofar*2, frank.toofar*2); 
      fill( 0, 0, 255 );
      ellipse( frank.x, frank.y, 4, 4);
  }
}// class Monster //



class Hero {
  String name="Hero";
  float x=door-100, y=floor, dx=-12, dy=15;
  int score=0;
  int lives=0;
  float toofar=100;            // Distance from hero to mouse, for movement.
  float tooclose=50;            // Too close for click.

  void move() {
    if (x>door && y<path)  y= path;
    if (x>width-20) x=width-20;
    if (x<20) x=20;
    if (y<horizon) y=horizon;
    if (y>height) y=height;
  }
  void show() {
    fill(0, 200, 255);
    rectMode(CENTER);
    rect(x, y, 40, 50);
    strokeWeight(1);
    noStroke();
    fill(255, 0, 0);
    text(name, x-20, y-12);
    fill(150, 0, 0);
    text(lives, x, y+12);
    //
    fill(255, 200, 200);
    ellipse(x, y-45, 35, 35);
    fill(255);
    ellipse(x-10, y-45, 15, 12);
    ellipse(x+10, y-45, 15, 12);
    fill(0, 0, 200);
    //
    int xx=0, yy=0, rr=3;
    float far=  me.near( mouseX, mouseY+35 );    // Is mouse near Hero?
    if (far<toofar) {        // No eyes, if too far.
      if (far<tooclose) {    // Stare, if too close.
        rr=7;
      } else {
        rr=5;
        // Eyes look toward mouse.
        if (mouseX < x-20) xx=  -4;
        if (mouseX > x+20) xx=  +4;
        if (mouseY < y-20) yy=  -3;
        if (mouseY > y+20) yy=  +3;
      } 
    }
    ellipse(x-10+xx, y-45+yy, rr, rr);
    ellipse(x+10+xx, y-45+yy, rr, rr);
    // Legs
    stroke(0,0,255);
    strokeWeight(12);
    line(x-15, y+25, x-15+xx, y+55);
    line(x+15, y+25, x+15+xx, y+55);
    stroke(0,150,150);    // Shoes
    line(x-15+xx, y+55, x-15+xx*3, y+55+yy*3 );
    line(x+15+xx, y+55, x+15+xx*3, y+55+yy*3 );
    noStroke();
  }
  float near( float xx, float yy ) {
    //// Is something near me?
    return dist( x, y, xx, yy );
  }
  void reset() {
    x=  door-40;
    y=  floor;
    lives++;
  }
  void upset() {
    x=  door + random(100);
    y=  path+random(50);
    lives++;
  }
  void go( float xx, float yy ) { 
    x=  x + (xx-x)/2;
    y=  y + (yy-y)/2;
  }
  void zones() {    //// Make collision zones visible.
    if (! hide){ 
      fill( 0, 0, 255, 30 );    // semi-transparent
      ellipse( me.x, me.y-40, me.toofar*2, me.toofar*2); 
      fill( 255, 0, 0, 90 );
      ellipse( me.x, me.y-40, me.tooclose*2, me.tooclose*2);
      fill( 0, 0, 255 );
      ellipse( me.x, me.y, 4, 4);
    } else {
      fill(0);
      text("ZONES",door,floor);
      rectMode(CENTER);
      fill( 0, 0, 255, 30 );    // semi-transparent
      rect( me.x, me.y-40, me.toofar*2, me.tooclose*2); 
      fill( 255, 0, 0, 90 );
      rect( me.x, me.y-40, me.tooclose*2, me.tooclose*2);
      fill( 0, 0, 255 );
      rect( me.x, me.y, 4, 4);
    }
  }
}// class Hero //

