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

/* Podtřída třídy AlienSprite.
   Přetěžuje metody:
      void playerHasMoved()   -- volá se při pohybu hráče
	  void move()             -- přesune vetřelce

   Vetřelec počítá cestu k hráči pomocí algoritmu pro hledání cesty
   známého pod označením A*. Cesta se uloží jako série průchozích bodů s tím, 
   že vetřelec se při každém volání metody update() přesune do dalšího v pořadí.

   K přepočítání cesty dochází v metodě playerHashMoved(), ovšem jen tehdy,
   pokud se hráč pohnul o MAX_MOVES políček, čímž ušetříme výpočetní kapacitu
   a hráči trošku usnadníme boj s vetřelci.

   Cesta se počítá znovu také v situaci, kdy vetřel prošel přes všechny body
   na aktuální cestě.
   Vetřelec se tedy chová přesně tak, jak potřebujeme: pronásleduje hráče. 
*/

import java.awt.*;
import java.util.*;


public class AlienAStarSprite extends AlienSprite
{
  // maximální počet pohybů hráče před dalším možným přepočítáním cesty
  private final static int MAX_MOVES = 5;  

  private int numPlayerMoves = 0;

  // seznam objektů typu Point (tj. cesta) směřujících ke hráči
  private ArrayList path;
  private int pathIndex = 0;

  

  public AlienAStarSprite(int x, int y, int w, int h, ImagesLoader imsLd,
                                    WorldDisplay wd)
  { super(x, y, w, h, imsLd, wd);
    path = new ArrayList();
  }  // konec metody: AlienQuadSprite()


  public void playerHasMoved(Point playerLoc)
  /* frekvenci generování cesty snížíme tak,
     že ji přepočítáváme vždy až poté, co se hráč přesune o MAX_MOVES políček.
  */
  { if (numPlayerMoves == 0)
      calcNewPath(playerLoc);
    else
      numPlayerMoves = (numPlayerMoves+1)%MAX_MOVES; 
  } // konec metody: playerHasMoved()


  private void calcNewPath(Point playerLoc)
  { 
    path = aStarSearch( getTileLoc(), playerLoc );
    pathIndex = 0;   // pro novou cestu vynuluj index
    // printPath();    // pro ladění
  }  // konec metody: calcNewPath()


  private void printPath()
  // vypiš objekty typu Points tvořící cestu
  {
    System.out.println("path: ");
    Point p;
    for(int i =0; i < path.size(); i++) {
      p = (Point) path.get(i);
      System.out.print("(" + p.x + "," + p.y + ") ");
    }
    System.out.println();
  }  // konec metody: printPath()


  protected void move()
  /* Přesuň se do následujícího bodu na předem spočítané cestě.
     Pokud vetřelec došel až na její konec, spočítej novou.
  */
  { if (pathIndex == path.size())  // došli jsme na konec aktuální cesty
      calcNewPath( world.getPlayerLoc() ); 
    Point nextPt = (Point) path.get(pathIndex);
    pathIndex++;
    int quad = whichQuadrant(nextPt);
    setMove(nextPt, quad);
  }  // konec metody: move()


  private ArrayList aStarSearch(Point startLoc, Point goalLoc)
  /* Odvozeno od pseudo-kódu uvedeného z knihy "Game Programming Gems" od Mike DeLoura		
  */
  {
    double newCost;
    TileNode bestNode, newNode;

    TileNode startNode = new TileNode(startLoc);  // počáteční uzel
    startNode.costToGoal(goalLoc);

    // vytvoř frontu 'otevřené' (open) a seznam 'uzavřené' (closed)
    TilesPriQueue open = new TilesPriQueue(startNode);
    TilesList closed = new TilesList();

    while (open.size() != 0) {  // dokud ještě zbývají uzly to investigate
      bestNode = open.removeFirst();
      if (goalLoc.equals( bestNode.getPoint() ))   // dosaženo cíle
        return bestNode.buildPatch();   // vrať cestu do cílového uzlu
      else {
        for (int i=0; i < NUM_DIRS; i++) {    // zkus všechny směry
          if ((newNode = bestNode.makeNeighbour(i, world)) != null) {
            newCost = newNode.getCostFromStart();
            TileNode oldVer;
            // má-li tato dlaždice levnější otevřený či uzavřený uzel 
            // tak nový uzel ignoruj
            if (((oldVer = open.findNode( newNode.getPoint())) != null) &&
                (oldVer.getCostFromStart() <= newCost))
              continue;
            else if (((oldVer = closed.findNode( newNode.getPoint())) != null) &&
                (oldVer.getCostFromStart() <= newCost))
              continue;
            else {   // ulož nový/vylepšený uzel a starý zahoď
              newNode.costToGoal(goalLoc);
              // smaž staré údaje (pokud existují)
              closed.delete( newNode.getPoint());  // nemusí udělat nic
              open.delete( newNode.getPoint());   // nemusí udělat nic
              open.add(newNode);
            }
          }
        } // konec bloku: for
      } // konec bloku: if-else
      closed.add(bestNode);
    }
    return null;   // cesta nenalezena
  }  // konec metody: aStarSearch()


}  // konec třídy: AlienAStarSprite
