
// JumperSprite.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* Sprite se může pohybovat doleva/doprava, skákat a zůstat
   na místě. Ve skutečnosti se horizontálně vůbec nepohybuje,
   takže požadavky na pohyb doleva a doprava jen mění některé
   příznaky, ne hodnotu proměnné locx.

   Sprite má cyklické obrázky pro pohyb doleva či doprava a 
   jediný obrázek, když stojí nebo skáče.

   Sprite dále uchovává své souřadnice v herním světě (xWorld, yWorld).
 
   Skákání má stoupací a padací složku. Stoupání a padání
   může být zastaveno nárazem do cihličky.

   Pohyb spritu doleva či doprava může být také zastaven nárazem
   do cihličky.

   Sprite začně padat pokud přejde přes okraj cihličky do volného prostoru.

   Dotazy ohledně cihliček (převážně se týkají detekce kolizí) se posílají
   objektu typu BricksManager.
*/

import java.awt.*;


public class JumperSprite extends Sprite
{
  private static double DURATION = 0.5;  // sekund
    // celkový čas průchodu všecmy obrázky

  private static final int NOT_JUMPING = 0;   
  private static final int RISING = 1;   
  private static final int FALLING = 2;   
  // pro vertMoveMode
  //  (v J2SE 1.5 bychom mohli použít výčtový typu)

  private static final int MAX_UP_STEPS = 8;
    // maximální počet kroků, při kterých může sprite při skoku stoupat vzhůru


  private int period;    // v ms; animační interval hry

  private boolean isFacingRight, isStill;

  private int vertMoveMode;
    /* může být NOT_JUMPING, RISING nebo FALLING */
  private int vertStep;   // vzdálenost, o kterou se sprite vertikálně posune v jednom kroku
  private int upCount;

  private BricksManager brickMan;
  private int moveSize;   // získali jsme od třídy BricksManager

  private int xWorld, yWorld;
    /* Aktuální pozice spritu v souřadnicích herního světa.
       x-ové hodnoty mohou být záporné. y-ové hodnoty budou mezi 0 a pHeight */
  

  public JumperSprite(int w, int h, int brickMvSz, BricksManager bm, 
                                        ImagesLoader imsLd, int p)
  {
    super(w/2, h/2, w, h, imsLd, "runningRight");
       // stojí uprostřed obrazovky, namířen směrem doprava
    moveSize = brickMvSz;
          // velikost pohybu je stejná jako u pásu cihliček

    brickMan = bm;
    period = p;
    setStep(0,0);     // žádný pohyb

    isFacingRight = true;
    isStill = true;

    /* Uprav y-ovou pozici spritu tak, aby stál na cihličce uprostřed
       její x-ové pozice. */
    locy = brickMan.findFloor(locx+getWidth()/2)-getHeight();
    xWorld = locx; yWorld = locy;    // ulož aktuální pozici

    vertMoveMode = NOT_JUMPING;
    vertStep = brickMan.getBrickHeight()/2;   
                // krok výskoku je dána polovinou výšky cihličky
    upCount = 0;
  }  // konec metody: JumperSprite()


  public void moveLeft()
  /* Zpracuje požadavek na pohyb spritu vlevo. Ve skutečnosti
     se ním nepohybuje, ale změní jeho obrázek a příznaky stavu. */
  { setImage("runningLeft");
    loopImage(period, DURATION);   // cyklicky procházej obrázky
    isFacingRight = false;  isStill = false;
  }

  public void moveRight()
  /* Zpracuje požadavek na pohyb spritu vpravo. Ve skutečnosti
     se ním nepohybuje, ale změní jeho obrázek a příznaky stavu. */
  { setImage("runningRight");
    loopImage(period, DURATION);   // cyklicky procházej obrázky
    isFacingRight = true;  isStill = false;
  }

  public void stayStill()
  /* Zpracuje požadavek na zastavení spritu. Zastaví animaci a 
     nastaví příznak stavu isStill na true. */
  { stopLooping(); 
    isStill = true;
  }


  public void jump()
  /* Sprite je požádán o skok. Nastaví vertMoveMode na RISING a změní
     jeho obrázek. Úpravu y-ové pozice provádí metoda updateSprite().
  */
  { if (vertMoveMode == NOT_JUMPING) { 
      vertMoveMode = RISING;
      upCount = 0;
      if (isStill) {    // změň obrázek, pouze stojí-li sprite na místě
        if (isFacingRight)
          setImage("jumpRight");
        else
          setImage("jumpLeft");
      }
    }
  } // konec metody: jump()


  public boolean willHitBrick()
  /* Otestuje, je-li následující x-ová pozice uvnitř cihličky.
     Ignoruje jakýkoliv y-ový pohyb. Tato metoda by se měla
     vždy volat před tím, než metoda updateSprite() provede
     skutečný pohyb. */
  {
    if (isStill)
      return false;   // nehýbeli-se, nemůže se s ničím srazit

    int xTest;   // pro testování nové x-ové pozice
    if (isFacingRight)   // přesun vpravo
      xTest = xWorld + moveSize;
    else // přesun vlevo
      xTest = xWorld - moveSize;

    // otestuj bod blízko základny spritu
    int xMid = xTest + getWidth()/2;
    int yMid = yWorld + (int)(getHeight()*0.8);   // použij aktuální y-ovou pozici

    return brickMan.insideBrick(xMid,yMid);  
  }  // konec metody: willHitBrick()


  public void updateSprite()
  /* I když se sprite v x-ovém směru nepohybuje, i tak
     musíme aktualizovat jeho souřadnice (xWorld, yWorld). 
     Pokud skáče, je třeba jeho y-ovou pozici aktualizovat
     pomocí metody moveVeertically(). Tato metoda by měla
     být volána až po kontrole kolize v metodě willHitBrick()
  */
  {
    if (!isStill) {    // pohyb
      if (isFacingRight)  // přesun vpravo
        xWorld += moveSize;
      else // přesun vlevo
        xWorld -= moveSize;
      if (vertMoveMode == NOT_JUMPING)   // pokud neskáče
        checkIfFalling();   // mohl se přesunout pryč do volného prostoru
    }

    // vertikální pohyb má dvě složky: RISING (stoupá) a FALLING (padá)
    if (vertMoveMode == RISING)
      updateRising();
    else if (vertMoveMode == FALLING)
      updateFalling();

    super.updateSprite();
  }  // konec metody: updateSprite()



  private void checkIfFalling()
  /* Pokud se sprite dostal do vzduchu pohybem vlevo nebo vpravo,
     přepni jej do padacího režimu. */
  {
    // může se sprite pohybovat dolů, chce-li?
    // testuj jeho středovou x-ovou souřadnici a y-ovou u základny
    int yTrans = brickMan.checkBrickTop( xWorld+(getWidth()/2), 
                     yWorld+getHeight()+vertStep, vertStep);
    // System.out.println("checkIfFalling: " + yTrans);
    if (yTrans != 0)   // ano, povolíme
      vertMoveMode = FALLING;   // nastav stav: padání
  }  // konec metody: checkIfFalling()



  private void updateRising()
  /* Stoupání bude pokračovat dokud se nedosáhne maximálního počtu
     vertikálních kroků nebo pokud sprite nenarazí na cihličku.
     Poté se sprite přepne do padacího režimu. */
  { if (upCount == MAX_UP_STEPS) {
      vertMoveMode = FALLING;   // uplně nahoře, nyní začni padat
      upCount = 0;
    }
    else {
      int yTrans = brickMan.checkBrickBase(xWorld+(getWidth()/2), 
                                      yWorld-vertStep, vertStep);
      if (yTrans == 0) {   // došlo ke srážce se spodní částí cihličky
        vertMoveMode = FALLING;   // začni padat
        upCount = 0;
      }
      else {   // může se pohybovat vzhůru o další krok
        translate(0, -yTrans);
        yWorld -= yTrans;   // aktualizuj pozici
        upCount++;
      }
    }
  }  // konec metody: updateRising()


  private void updateFalling()
  /* Padání bude pokračovat dokud sprite nenarazí na vrchní stranu
     cihličky. Hra běží pouze s pásem cihliček, který má celistvou
     podlahu, takže sprite nepropadne dolů.

     Do padacího režimu lze vstoupit i bez odpovídající stoupací
     sekvence, a to například tehdy, když sprite projde za okraj útesu.
  */
  {
    int yTrans = brickMan.checkBrickTop(xWorld+(getWidth()/2), 
                        yWorld+getHeight()+vertStep, vertStep);
    if (yTrans == 0)   // došlo ke srážce s horní části nějaké cihličky
      finishJumping();
    else {    // může pokračovat dolů o další krok
      translate(0, yTrans);
      yWorld += yTrans;   // aktualizuj pozici
    }
  }  // konec metody: updateFalling()


  private void finishJumping()
  {
    vertMoveMode = NOT_JUMPING;
    upCount = 0;

    if (isStill) {    // změn na obrázek pro běh, ale ještě pro skok
      if (isFacingRight)
        setImage("runningRight");
      else    // směřuje vlevo
        setImage("runningLeft");
    }
  }  // konec metody: finishJumping()


}  // konec třídy: JumperSprite

