 /* '12 Game.cpp' */

 /* Plik komplementarnego nagłówka CGame  */
#include "12 Game.h"

 /* Inicjalizacja składników na stan początkowy */
CGame::CGame ()
{
 m_Console    = NULL;
 m_GameStatus = GameSplashScreen;
 m_LastAction = 0;
 m_Monsters   = NULL;
}
 
 /* Inicjalizacja składników na stan początkowy z informacjami o konsoli */
CGame::CGame (ConLib * Console)
{
 m_Console    = Console;
 m_GameStatus = GameSplashScreen;
 m_LastAction = 0;
 m_Monsters   = NULL;
}

 /* Destruktor domyślny */
CGame::~CGame ()
{
 m_Console    = NULL;
 m_GameStatus = GameSplashScreen;
 m_LastAction = 0;
 m_Monsters   = NULL;
}

 /* Ustawia wskaźnik do konsoli */
void CGame::SetConsole (ConLib * Console)
{
 m_Console    = Console;
}

 /* Zwraca status gry */
int CGame::GetStatus (void)
{
 return m_GameStatus;
}


 /* Pokazuje ekran powitalny z instrukcją gry */
void CGame::ShowSplash (void)
{
 m_Console->Clear ();
 m_Console->OutputString ("\tWitaj w grze Potwor 1.0 \n\n");
 m_Console->OutputString ("Gra w Potwora jest bardzo prosta. \n\n");

 m_Console->OutputString ("Celem gry jest zabicie wszystkich \n");
 m_Console->OutputString ("potworow. Dwa lub wiecej potworow \n");
 m_Console->OutputString ("ginie, kiedy znajda sie na tym samym \n");
 m_Console->OutputString ("polu. Ty rowniez tracisz zycie \n");
 m_Console->OutputString ("jezeli znajdziesz sie na polu, \n");
 m_Console->OutputString ("na ktorym jest potwor. Poruszasz sie \n");
 m_Console->OutputString ("za pomoca klawiatury numerycznej w osmiu \n");
 m_Console->OutputString ("mozliwych kierunkach. Kiedy nacisniesz \n");
 m_Console->OutputString ("Insert wykonasz skok na losowe miejsce \n");
 m_Console->OutputString ("planszy.\n\n");

 m_Console->SetTextColor (ConRed);
 m_Console->OutputString ("NOTE: Aby grac wylacz NumLock.\n\n");
 m_Console->SetTextColor (ConRed | ConGreen | ConBlue);

 m_Console->OutputString ("Dostepne sa trzy poziomy trudnosci::\n\n");
 m_Console->OutputString (" Latwy  :  Potwory  = 10   Arena = 25*15\n");
 m_Console->OutputString ("           Zycia    = 4    Skoki = 3\n");
 m_Console->OutputString (" Sredni :  Potwory  = 20   Arena = 35*18\n");
 m_Console->OutputString ("           Zycia    = 3    Skoki = 2\n");
 m_Console->OutputString (" Trudny :  Potwory  = 30   Arena = 50*23\n");
 m_Console->OutputString ("           Zycia    = 2    Skoki = 1");
}

 /* Pokazuje menu główne */
void CGame::ShowMenu (void)
{
 COORD Position;

 m_Console->SetBackgroundColor (0);
 m_Console->SetTextColor (ConRed);
 m_Console->Clear ();

 m_Console->SetBackgroundColor (ConRed | ConGreen | ConBlue);

 m_Console->OutputString ("                                        \n");
 m_Console->OutputString ("         Potwor  wersja 1.0            \n");
 m_Console->OutputString ("                                        ");

 m_Console->SetBackgroundColor (0);
 m_Console->SetTextColor (ConRed | ConGreen | ConBlue);

 Position.X = 1;
 Position.Y = 4;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("Co wybierasz: ");
 
 Position.X = 3;
 Position.Y = 6;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("1 - Zacznij nowa gra - poziom latwy");
 Position.Y = 7;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("2 - Zacznij nowa gra - poziom sredni");
 Position.Y = 8;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("3 - Zacznij nowa gra - poziom trudny");
 Position.Y = 10; /* print */
 m_Console->SetPosition (Position);
 m_Console->OutputString ("L - Zaladuj gre");

 Position.Y = 12;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("Q - Wyjscie");
}

 /* Pokazuje ekran gry */
void CGame::ShowGame (void)
{
 COORD Position;
 int Monster;

  /* Rysuje pozycje gracza */
 m_Console->SetBackgroundColor (0);
 m_Console->SetTextColor (ConGreen);

 m_Player.GetPosition (&Position);

 m_Console->SetPosition (Position);
 m_Console->OutputString ("P");

  /* Rysuje plansze */
 int FieldX, FieldY;
 m_Console->SetBackgroundColor (ConRed | ConGreen | ConBlue);
 m_Console->SetTextColor (ConRed | ConGreen | ConBlue);

 for (FieldY = 0; FieldY <= m_Arena.Y; FieldY++)
 {
  if ( (FieldY == 0) || (FieldY == m_Arena.Y) )
  {
   for (FieldX = 0; FieldX <= m_Arena.X; FieldX++)
   {
    Position.X = FieldX;
    Position.Y = FieldY;
    m_Console->SetPosition (Position);
    m_Console->OutputString ("#");
   }
  }
  else
  {
   Position.X = 0;
   Position.Y = FieldY;
   m_Console->SetPosition (Position);
   m_Console->OutputString ("#");
   Position.X = m_Arena.X;
   Position.Y = FieldY;
   m_Console->SetPosition (Position);
   m_Console->OutputString ("#");
  }
 }

  /* Rysuje potwory */
 m_Console->SetBackgroundColor (0);
 m_Console->SetTextColor (ConRed);
 for (Monster = 0; Monster < m_MonstersNumber; Monster++)
 {
  if (m_Monsters [Monster].X != 0)
  {
   m_Console->SetPosition (m_Monsters [Monster]);
   m_Console->OutputString ("M");
  }
 }

  /* Pokazuje życia i wynik */
 char Buffer [100];

 sprintf (Buffer, "Zycia: %d   Wynik: %d   Skoki: %d   S - Zapis gry", 
          m_Player.GetLives () - 1, m_Player.GetScore (), 
          m_Player.GetLeaps ()); /* wyswietla*/
 Position.X = 0;
 Position.Y = 24;
 m_Console->SetPosition (Position);
 m_Console->SetTextColor (ConRed | ConGreen | ConRed);
 m_Console->OutputString (Buffer);

}

 /* Pokazuje okno zwyciestwa */
void CGame::ShowWon (void)
{
 ShowGame ();

 COORD Position;

 Position.X = 20;
 Position.Y = 11;
 m_Console->SetPosition (Position);

 m_Console->SetBackgroundColor (ConGreen);
 m_Console->SetTextColor (ConRed);

 m_Console->OutputString ("########################################");
 Position.Y = 12;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("#               Gratulacje!            #");
 Position.Y = 13;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("#        Zabiles wszystkie potwory     #");
 Position.Y = 14;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("########################################");
}

 /* Pokazuje okno utraty życia */
void CGame::ShowLostLife (void)
{
 ShowGame ();

 COORD Position;

 Position.X = 20;
 Position.Y = 11;
 m_Console->SetPosition (Position);

 m_Console->SetBackgroundColor (ConGreen);
 m_Console->SetTextColor (ConRed);

 m_Console->OutputString ("########################################");
 Position.Y = 12;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("#           Straciles zycie            #");
 Position.Y = 13;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("########################################");
}

 /* Pokazuje okno przegranej */
void CGame::ShowLost (void)
{
 ShowGame ();

 COORD Position;

 Position.X = 20;
 Position.Y = 11;
 m_Console->SetPosition (Position);

 m_Console->SetBackgroundColor (ConGreen);
 m_Console->SetTextColor (ConRed);

 m_Console->OutputString ("########################################");
 Position.Y = 12;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("#             Niestety   !             #");
 Position.Y = 13;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("#      Straciles wszystkie zycia       #");
 Position.Y = 14;
 m_Console->SetPosition (Position);
 m_Console->OutputString ("########################################");
}

 /* Ukazuje informację przy wyjściu z gry */
void CGame::ShowExit (void)
{
 m_Console->SetBackgroundColor (0);
 m_Console->SetTextColor (ConRed | ConGreen | ConBlue);
 m_Console->Clear ();
 m_Console->OutputString ("\n   Potwor 1.0 \n\n\n");
 m_Console->OutputString (" by: Bruno Sousa (bsousa@fireworks");
 m_Console->OutputString ("-interactive.com)\n\n\n\n");
 m_Console->OutputString ("Dzieki za gre!\n\n\n");
 m_Console->OutputString ("I pamietaj, trzymaj sie z dala od dragow.\n\n");
}

 /* Pokazuje odpowiedni ekran w zależności od statusu gry */
void CGame::Show (void)
{
 m_Console->SetBackgroundColor (0);
 m_Console->SetTextColor (ConRed | ConGreen | ConBlue);
 m_Console->Clear ();

 switch (m_GameStatus)
 {
 case GameMainMenu:
  ShowMenu ();
 break;

 case GameRunning:
  ShowGame ();
  break;

 case GameWon:
  ShowWon ();
  break;

 case GameLostLife:
  ShowLostLife ();
  break;

 case GameLost:
  ShowLost ();
  break;

 case GameExit:
  ShowExit ();
  break;

 case GameSplashScreen:
  ShowSplash ();
  break;

 default:
  break;
 }
}

 /* Rozpoczyna nową grę */
void CGame::StartNewGame (int Difficulty)
{
 int Monster;

 COORD Position;
 
 m_GameStatus = GameRunning;

  /* Ustawia poziom trudności */
 switch (Difficulty)
 {
 case GameEasy:
  m_MonstersNumber = 10;
  m_Player.SetLives (4);
  m_Player.SetLeaps (3);
  m_Arena.X = 25;
  m_Arena.Y = 15;
  break;
 case GameMedium:
  m_MonstersNumber = 25;
  m_Player.SetLives (3);
  m_Player.SetLeaps (2);
  m_Arena.X = 35;
  m_Arena.Y = 18;
  break;
 case GameDifficult:
  m_MonstersNumber = 35;
  m_Player.SetLives (2);
  m_Player.SetLeaps (1);
  m_Arena.X = 50;
  m_Arena.Y = 23;
  break;
 }

  /* Tworzy gracza */
 m_Player.RandomLeap (m_Arena);
 m_Player.GetPosition (&Position);
 m_Player.SetScore (0);

  /* Tworzy potwory */
 m_Monsters = new COORD [m_MonstersNumber];
 srand (time (NULL));

  /* Oblicza losowe pozycje potworów */
 for (Monster = 0; Monster < m_MonstersNumber; Monster++)
 {
   /* Upewnia się, że pozycja różni się od pozycji gracza */
  do 
  {
   m_Monsters [Monster].X = (rand () % (m_Arena.X - 1)) + 1;
   m_Monsters [Monster].Y = (rand () % (m_Arena.Y - 1)) + 1;
  }
  while ( (m_Monsters [Monster].X == Position.X) && 
          (m_Monsters [Monster].Y == Position.Y) );
 }
}

 /* Odbieranie posunięć gracza */
int CGame::GetAction (void)
{ 
  /* Pobranie informacji od użytkownika */
 m_LastAction = m_Console->GetKey ();

 return m_LastAction;
}

 /* Przemieść potwory zgodnie z pozycją gracza */
void CGame::MoveMonsters (void)
{
 COORD Distance, Position;
 int Monster;

 m_Player.GetPosition (&Position);

 for (Monster = 0; Monster < m_MonstersNumber; Monster++)
 {
   /* Sprawdź czy potwór nie żyje */
  if (m_Monsters [Monster].X != 0)
  {
   Distance.X = Position.X - m_Monsters [Monster].X;
   Distance.Y = Position.Y - m_Monsters [Monster].Y;

    /* Upewnij się, że ruch nastąpił tylko o jedno pole */
   if (Distance.X > 0)
   {
    Distance.X = 1;
   }
   if (Distance.X < 0)
   {
    Distance.X = -1;
   }
   if (Distance.Y > 0)
   {
    Distance.Y = 1;
   }
   if (Distance.Y < 0)
   {
    Distance.Y = -1;
   }
   
    /* Przemieść potwory */
   m_Monsters [Monster].X += Distance.X;
   m_Monsters [Monster].Y += Distance.Y;
  }
 }
}


 /* Przetwarzaj ekran powitalny */
void CGame::ProcessSplash (void)
{
  /* Jeżeli użytkownik nacisnął klawisz, przejdź do głównego menu */
 if (m_LastAction)
 {
  m_GameStatus = GameMainMenu;
 }
}

 /* Pobiera opcję z menu i albo wychodzi albo rozpoczyna nową grę */
void CGame::ProcessMenu (void)
{
 switch (m_LastAction)
 {
  /* Wyjście z gry */
 case VK_ESCAPE:
 case 'Q':
 case 'q':
  m_GameStatus = GameExit;
  break;

  /* Rozpoczęcie nowej gry */
 case '1':
  StartNewGame (GameEasy);
  m_GameStatus = GameRunning;
  break;
 case '2':
  StartNewGame (GameMedium);
  m_GameStatus = GameRunning;
  break;
 case '3':
  StartNewGame (GameDifficult);
  m_GameStatus = GameRunning;
  break;
 case 'L':
 case 'l':
  StartNewGame (GameEasy);
  Load ();
  m_GameStatus = GameRunning;
  break;

 default:
  break;
 }
}

 /* Przemieszcza gracza i potwory */
void CGame::ProcessGame (void)
{
 COORD Movement;
 int Monster, MonstersAlive;

 Movement.X = 0;
 Movement.Y = 0;

  /* Przemieszcza gracza */
 switch (m_LastAction)
 { 
 case VK_UP:
  Movement.Y = -1;
  break;
 case VK_DOWN:
  Movement.Y = 1;
  break;
 case VK_LEFT:
  Movement.X = -1;
  break;
 case VK_RIGHT:
  Movement.X = 1;
  break;

 case VK_HOME:
  Movement.X = -1;
  Movement.Y = -1;
  break;
 case VK_PRIOR:
  Movement.X = 1;
  Movement.Y = -1;
  break;
 case VK_END:
  Movement.X = -1;
  Movement.Y = 1;
  break;
 case VK_NEXT:
  Movement.X = 1;
  Movement.Y = 1;
  break;
 case VK_INSERT:
  if (m_Player.GetLeaps () > 0)
  {
   m_Player.RandomLeap (m_Arena);
   m_Player.SetLeaps (m_Player.GetLeaps () - 1);
  }
  break;
 case VK_ESCAPE:
  EndGame ();
  m_GameStatus = GameMainMenu;
  break;

 case 's':
 case 'S':
  Save ();
  break;
 }

  /* Był ruch */
 if ( (Movement.X != 0) || (Movement.Y != 0) )
 {
  COORD PlayerPosition;
  m_Player.GetPosition (&PlayerPosition);

   /* Jeżeli ruch był w obrębie areny */
  if ( (Movement.X + PlayerPosition.X > 0) && 
       (Movement.Y + PlayerPosition.Y > 0) &&
       (Movement.X + PlayerPosition.X < m_Arena.X) &&
       (Movement.Y + PlayerPosition.Y < m_Arena.Y) )
  {
   m_Player.Move (Movement);
  }

   /* Wykonaj AI potworów i sprawdź kolizje */
  MoveMonsters ();
  CheckCollisions ();

   /* Sprawdź czy któryś z potworów jest jeszcze żywy */
  MonstersAlive = 0;
  for (Monster = 0; Monster < m_MonstersNumber; Monster ++)
  {
    /* Sprawdź czy potwór umarł */
   if (m_Monsters [Monster].X != 0)
   {
    MonstersAlive = 1;
    break;
   }
  }
  if (MonstersAlive == 0)
  {
   m_GameStatus = GameWon;
  }
 }
}

 /* Sprawdź kolizję pomiędzy potworem a graczem */
void CGame::CheckCollisions ()
{
 COORD Position;
 int MonsterA, MonsterB;

 m_Player.GetPosition (&Position);

 for (MonsterA = 0; MonsterA < m_MonstersNumber; MonsterA ++)
 {
   /* Sprawdź czy potwór umarł */
  if (m_Monsters [MonsterA].X != 0)
  {
    /* Sprawdź kolizję z graczem */
   if ( (m_Monsters [MonsterA].X == Position.X) &&
        (m_Monsters [MonsterA].Y == Position.Y) )
   {
    m_Monsters [MonsterA].X = 0;
    m_Monsters [MonsterA].Y = 0;

     /* Sprawdź czy graczowi pozostały jeszcze jakieś życia */
    if (m_Player.GetLives () - 1 <= 0)
    {
     m_GameStatus = GameLost;
    }
    else
    {
     m_GameStatus = GameLostLife;
    }
    return;
   }
    /* Sprawdź kolizje pomiędzy potworami */
   for (MonsterB = MonsterA+1; MonsterB < m_MonstersNumber; MonsterB++)
   {
     /* Sprawdź czy potwór umarł */
    if (m_Monsters [MonsterB].X != 0)
    {
      /* Sprawdź kolizje z potworami */
     if ( (m_Monsters [MonsterA].X == m_Monsters [MonsterB].X) &&
          (m_Monsters [MonsterA].Y == m_Monsters [MonsterB].Y) )
     {
      m_Monsters [MonsterA].X = m_Monsters [MonsterB].X = 0;
      m_Monsters [MonsterA].Y = m_Monsters [MonsterB].Y = 0;
      m_Player.SetScore (m_Player.GetScore () + 15);
     }
    }
   }
  }
 }
}

 /* Zakończ grę i powróć do głównego menu */
void CGame::ProcessWon (void)
{
  /* Jeżeli użytkownik nacisnął klawisz, przejdź do głównego menu */
 if (m_LastAction)
 {
  m_GameStatus = GameMainMenu;
 }

 EndGame ();
}

 /* Zakończ grę */
void CGame::EndGame (void)
{
 if (m_Monsters != NULL)
 {
  delete [] m_Monsters;
 }
 m_Monsters = NULL;
}

 /* Odejmuje graczowi życie */
void CGame::ProcessLostLife (void)
{
 int IsValid = 0;
 int Monster;
 COORD Position;

  /* Odejmij graczowi życie; jeżeli nie ma już więcej żyć, zakończ grę */
 m_Player.SetLives (m_Player.GetLives () - 1);
 if (m_Player.GetLives () - 1 <= -1)
 {
  m_GameStatus = GameLost;
 }
 else
 {
  m_GameStatus = GameRunning;
  IsValid = 0;
   /* Oblicz losową pozycję dla gracza */
  do 
  {
   m_Player.RandomLeap (m_Arena);
   m_Player.GetPosition (&Position);
  
    /* Upewnij się, że pozycja różni się od pozycji innych potworów */
   for (Monster = 0; Monster < m_MonstersNumber; Monster++)
   {
     /* Sprawdź czy potwór umarł */
    if (m_Monsters [Monster].X != 0)
    {
     if ( (m_Monsters [Monster].X != Position.X) &&
          (m_Monsters [Monster].Y != Position.Y) )
     {
      IsValid = 1;
     }
     else
     {
      IsValid = 0;
     }
    }
   }
  }
  while ( IsValid == 0 );
 }
}

 /* Zakończ grę i powróć do głównego menu */
void CGame::ProcessLost (void)
{
  /* Jeżeli użytkownik nacisnął klawisz, przejdź do głównego menu */
 if (m_LastAction)
 {
  m_GameStatus = GameMainMenu;
 }

 EndGame ();
 Show ();
}

 /* Ogólna funkcja wykonująca wszystkie zadania w danej turze */
void CGame::Process (void)
{
  /* Jako, że na początku misu być ukazany ekran powitalny, trzeba
     wymusić jego ukazanie się ponieważ niema żadnych zdarzeń oczekujących */
 if (m_GameStatus == GameSplashScreen)
 {
  Show ();
 }

  /* Jeżeli użytkownik naciśnie klawisz, reaguj odpowiednio */
 if (GetAction ())
 {
  switch (m_GameStatus)
  {
  case GameMainMenu:
   ProcessMenu ();
   break;

  case GameRunning:
   ProcessGame ();
   break;

  case GameWon:
   ProcessWon ();
   break;

  case GameLostLife:
   ProcessLostLife ();
   break;

  case GameLost:
   ProcessLost ();
   break;

  case GameSplashScreen:
   ProcessSplash ();
   break;

  default:
   break;
  }
  Show ();
 }
}

 /* Ładuje uprzednio zapisaną grę */
void CGame::Load (void)
{
 fstream  File;

 File.open ("Monster.sav", ios::in | ios::binary);

 if (File.is_open ())
 {
  COORD PlayerPosition;
  short PlayerLives;
  int   PlayerScore;
  int   PlayerLeaps;

   /* Załaduj grę z pliku */
  File.read ((char *) &m_Arena, sizeof (COORD));
  File.read ((char *) &PlayerPosition, sizeof (COORD));
  File.read ((char *) &PlayerLives, sizeof (short));
  File.read ((char *) &PlayerScore, sizeof (int));
  File.read ((char *) &PlayerLeaps, sizeof (int));
  File.read ((char *) &m_MonstersNumber, sizeof (int));
  if (m_Monsters != NULL)
  {
   delete [] m_Monsters;
  }
  m_Monsters = new COORD [m_MonstersNumber];
  File.read ((char *) m_Monsters, sizeof (COORD) * m_MonstersNumber);

   /* Ustaw informacje w klasie CPlayer */
  m_Player.SetPosition (&PlayerPosition);
  m_Player.SetLives (PlayerLives);
  m_Player.SetLeaps (PlayerLeaps);
  m_Player.SetScore (PlayerScore);

		File.close ();
 }
}

 /* Zapisuje bieżącą grę */
void CGame::Save (void)
{
 fstream  File;

 File.open ("Monster.sav", ios::out | ios::binary);

 if (File.is_open ())
 {
  COORD PlayerPosition;
  short PlayerLives;
  int   PlayerScore;
  int   PlayerLeaps;

   /* Pobiera informacje z klasy CPlayer */
  m_Player.GetPosition (&PlayerPosition);
  PlayerLives = m_Player.GetLives ();
  PlayerLeaps = m_Player.GetLeaps ();
  PlayerScore = m_Player.GetScore ();

   /* Zapisuje grę do pliku */
  File.write ((char *) &m_Arena, sizeof (COORD));
  File.write ((char *) &PlayerPosition, sizeof (COORD));
  File.write ((char *) &PlayerLives, sizeof (short));
  File.write ((char *) &PlayerScore, sizeof (int));
  File.write ((char *) &PlayerLeaps, sizeof (int));
  File.write ((char *) &m_MonstersNumber, sizeof (int));
  File.write ((char *) m_Monsters, sizeof (COORD) * m_MonstersNumber);

		File.close ();
 }
}
