unit uGame;

interface

uses 
  BasicGame, 
  ElysionLegacy, 
  SDL_gfx,
  SysUtils,
  uBasic,
  uGlobal,
  uGameUnit,
  uGameObject;

type 
  TGame = class(TBasicGame)
  private
    GameState: TGameState;
	FBackground: TelSprite;
	FFont: TelTrueTypeFont;
	FRounds: Integer;
	
	{$IFNDEF DARWIN}
	Music: TelMusic;
	Sound: array[1..2, 0..2] of TelSound;
	{$ENDIF}
	
	MainMenuButton: array[0..2] of TelButton;
	SettingButton: array[0..1] of TelButton;
	LvlSelButton: array[0..4] of TelButton;
	
	ShowInfo: Boolean;
	
	GUI_Button: array[0..1] of TelSprite;
	
	procedure DrawGUI;
	
	//
	procedure GameLogic;
	
	procedure NextRound;
	
	function InvPlayerTurn: Byte; 
	
	procedure Reset;
  public
    PUnit: array[1..2, 0..2] of TGameUnit;
	GObject: array[0..5] of TGameObject;
    PlayerTurn: Byte;
  
    constructor Create;
	destructor Destroy; Override;
	
	procedure Free;

	// Load graphical objects
    procedure Initialize(); Override;

	// Render stuff on screen
    procedure Render(); Override;
	
	// Update -> Event inputs
	procedure Update(); Override;
	
  published
    property Rounds: Integer read FRounds;
  end;
  
implementation

constructor TGame.Create;
begin
  GameState := gsIntro;
  
  FBackground := TelSprite.Create;
  GUI_Button[0] := TelSprite.Create;
  GUI_Button[1] := TelSprite.Create;
  
  FFont := TelTrueTypeFont.Create;

  
  inherited Create(800, 600, 32, false);
end;

destructor TGame.Destroy;
begin
  if Self <> Nil then Free;
end;

procedure TGame.Free;
var i, j: Integer;
begin
  FBackground.Free;
  GUI_Button[0].Free;
  GUI_Button[1].Free;
  
  for i := 1 to 2 do
  begin
    for j := 0 to 2 do PUnit[i, j].Free;
  end;
  
  for i := 0 to 5 do GObject[i].Free;

end;

procedure TGame.DrawGUI;
var i: Integer;
begin
  boxRGBA(ActiveSurface.SDL_Surface, 0, 0, 800, 40, 0, 0, 0, 128);
  
  GUI_Button[0].Draw;
  GUI_Button[1].Draw;
  
  // Handles the top menu inputs
  
  // Sprite Coloring in ElysionLegacy needs fixing -> Do it if time left
  //if GUI_Button[0].OnMouseHover then GUI_Button[0].Color := makeCol(255, 255, 255, 128) else GUI_Button[0].Color := makeCol(0, 0, 0, 0);
  
  if GUI_Button[0].OnClick then GameState := gsMainMenu;
  if GUI_Button[1].OnClick then NextRound;
  
  
  RectangleRGBA(ActiveSurface.SDL_Surface, (ActiveSurface.Cursor.X div GRID_SIZE) * GRID_SIZE, ((ActiveSurface.Cursor.Y - TOP_MENU) div GRID_SIZE) * GRID_SIZE + TOP_MENU, (ActiveSurface.Cursor.X div GRID_SIZE) * GRID_SIZE + GRID_SIZE, ((ActiveSurface.Cursor.Y - TOP_MENU) div GRID_SIZE) * GRID_SIZE + TOP_MENU + GRID_SIZE,  255, 255, 0, 128);

  FFont.TextOut(makeP2D(4, 4), 'Round: ' + IntToStr(Rounds));
  FFont.TextOut(makeP2D(4, 22), 'Player '+ IntToStr(PlayerTurn) +'''s Turn');
  
  for i := 0 to 2 do
  begin
   
    if PUnit[PlayerTurn, i].Marked then
	begin
	  //boxRGBA(ActiveSurface.SDL_Surface, 200, 0, 200, 40, 32, 32, 32, 255);
	  FFont.TextOut(makeP2D(204, 4), 'HP: ' + IntToStr(PUnit[PlayerTurn, i].Hitpoints.Value) + '/' + IntToStr(PUnit[PlayerTurn, i].Hitpoints.Max));
	  FFont.TextOut(makeP2D(204, 22), 'EXP: ' + IntToStr(PUnit[PlayerTurn, i].Experience.Value) + '/' + IntToStr(PUnit[PlayerTurn, i].Experience.Max));
	end;
	
  end;
  
  
  
  if ShowInfo then FFont.TextOut(makeP2D(0, 0), 'FPS: ' + IntToStr(ActiveSurface.FPS) + ' - (C) Johannes Stein, 2009; http://www.freeze-dev.de', avButtom, ahCenter);  
end;

procedure TGame.GameLogic;
var i, j: Integer;
begin
  // Checks if units are dead
  for i := 1 to 2 do
  begin
    for j := 0 to 2 do 
	begin
	  if PUnit[i, j].Hitpoints.Value <= PUnit[i, j].Hitpoints.Min then PUnit[i, j].Dead := true;
	  if PUnit[i, j].Experience.Value = PUnit[i, j].Experience.Max then PUnit[i, j].SetLevel(PUnit[i, j].Level + 1);
    end;  
  end;
  
  // If Player 1 units are dead
  if (PUnit[1, 0].Dead) and (PUnit[1, 1].Dead) and (PUnit[1, 2].Dead) then FFont.TextOut(makeP2D(300, 14), 'Player 1 wins!');
  
  // If Player 2 units are dead
  if (PUnit[2, 0].Dead) and (PUnit[2, 1].Dead) and (PUnit[2, 2].Dead) then FFont.TextOut(makeP2D(300, 14), 'Player 2 wins!');
  
end;

procedure TGame.NextRound;
var i: Integer;
begin

  // Checks if units can move
  if (PUnit[PlayerTurn, 0].CanMove) or
     (PUnit[PlayerTurn, 1].CanMove) or
	 (PUnit[PlayerTurn, 2].CanMove) then ;
  
  
  // Give those units their move and attack points back
  for i := 0 to 2 do
  begin
    case PUnit[PlayerTurn, i].UnitType of
	  utArcher:
	  begin
	    PUnit[PlayerTurn, i].AttackPoints := 2;
	    PUnit[PlayerTurn, i].MovePoints := 2;
	  end;
	  utSwordsman:
	  begin
	    PUnit[PlayerTurn, i].AttackPoints := 1;
	    PUnit[PlayerTurn, i].MovePoints := 2;
	  end;
	  utPikeman:
	  begin
	    PUnit[PlayerTurn, i].AttackPoints := 1;
	    PUnit[PlayerTurn, i].MovePoints := 3;
	  end;
	end;
  end;
  
  // Switch players turn
  if PlayerTurn = 1 then PlayerTurn := 2 else PlayerTurn := 1;
  
  for i := 0 to 2 do
  begin
    PUnit[PlayerTurn, i].EnemyUnit := true;
	PUnit[PlayerTurn, i].EnemyUnit := false;
  end;
  
  FRounds := FRounds + 1;
end;

function TGame.InvPlayerTurn: Byte;
begin
  if PlayerTurn = 1 then Result := 2 else Result := 1;
end;

procedure TGame.Reset;
var i, j: Integer;
begin
  ResetGridBlock;
  
  PlayerTurn := 1;
  
  FRounds := 1;
  
  for i := 1 to 2 do
  begin
    for j := 0 to 2 do
    begin
	  PUnit[i, j].Marked := false;
	  PUnit[i, j].Dead := false;
	
      if i = 1 then 
	  begin
	    PUnit[i, j].SetGridPos(makeP2D(Random(GetGridWidth div 2), Random(GetGridHeight - 1)));
		PUnit[i, j].EnemyUnit := true;
	  end;
	  
	  if i = 2 then 
	  begin
	    PUnit[i, j].SetGridPos(makeP2D(10 - Random(GetGridWidth div 2), Random(GetGridHeight - 1)));
		PUnit[i, j].EnemyUnit := false;
      end;
	end;
  end;
  
  for i := 0 to 5 do GObject[i].SetGridPos(makeP2D(Random(GetGridWidth - 1), Random(GetGridHeight - 1)));
  
end;

procedure TGame.Initialize;
var i: Integer;
begin
  ActiveSurface.Caption := 'Cavern Defense (LD # 15 Entry - http://www.ludumdare.com)';
  ActiveSurface.ShowCursor;
  
  FBackground.LoadFromFile(GetResImgPath + 'map1.jpg');
  FBackground.Generate;
  
  FFont.LoadFromFile(GetStdFont, 10);
  FFont.SetColor(makeCol(255, 255, 255));
  FFont.SetRenderType(rtBlended);
  FFont.SetTextStyle(tsNormal);
  FFont.SetFontStyle([fsBold]);
  
  GUI_Button[0].LoadFromFile(GetResImgPath + 'ingame_btns.png', makeRect(0, 0, 64, 32));
  GUI_Button[0].Transparent := true;
  GUI_Button[0].Generate(gmRGB);
  GUI_Button[0].Position := makeP2D(Self.Width - GUI_Button[0].Width - 4, 4);
  
  GUI_Button[1].VirtualClone(GUI_Button[0]);
  GUI_Button[1].ClipImage(makeRect(64, 0, 64, 32));
  GUI_Button[1].Position := makeP2D(Self.Width - (GUI_Button[1].Width * 2) - 4 - 4, 4);
  
  {$IFNDEF DARWIN}
  Music := TelMusic.Create;
  Music.LoadFromFile(GetResSndPath + 'music.ogg');
  Music.SetVolume(20);
  Music.Play;
  
  Sound[1, 0] := TelSound.Create;
  Sound[1, 1] := TelSound.Create;
  Sound[1, 2] := TelSound.Create;
  
  Sound[2, 0] := TelSound.Create;
  Sound[2, 1] := TelSound.Create;
  Sound[2, 2] := TelSound.Create;
  
  Sound[2, 0].LoadFromFile(GetResSndPath + 'human1.wav');
  Sound[2, 1].LoadFromFile(GetResSndPath + 'human2.wav');
  Sound[2, 2].LoadFromFile(GetResSndPath + 'human3.wav');
  Sound[1, 0].LoadFromFile(GetResSndPath + 'orc1.wav');
  Sound[1, 1].LoadFromFile(GetResSndPath + 'orc2.wav');
  Sound[1, 2].LoadFromFile(GetResSndPath + 'orc3.wav');
  {$ENDIF}
  
  for i := 0 to 5 do 
  begin
    if i <= 2 then
	begin
	  MainMenuButton[i] := TelButton.Create;
	  MainMenuButton[i].LoadFromFile(GetResImgPath + 'button.png');
	  MainMenuButton[i].LoadFont(GetStdFont, 10);
	  MainMenuButton[i].Position := makeP2D((800 - 250) div 2, 100 * i);
	end;
	GObject[i] := TGameObject.Create;
  end;
  
  MainMenuButton[0].Caption := 'New game';
  MainMenuButton[1].Caption := 'Instructions';
  MainMenuButton[2].Caption := 'Exit';
  
  PUnit[1, 0] := TGameUnit.Create(raOrc, utArcher, 1, 1);
  PUnit[1, 1] := TGameUnit.Create(raOrc, utPikeman, 1, 1);
  PUnit[1, 2] := TGameUnit.Create(raOrc, utSwordsman, 1, 1);
  
  PUnit[2, 0] := TGameUnit.Create(raHuman, utArcher, 1, 1);
  PUnit[2, 1] := TGameUnit.Create(raHuman, utPikeman, 1, 1);
  PUnit[2, 2] := TGameUnit.Create(raHuman, utSwordsman, 1, 1);
  
  Reset;
end;

//
// Never used procedures in procedures before
// But it's actually quite practical
procedure TGame.Render;
  
  procedure DrawIntroScene;
  begin
    FFont.TextOut(makeP2D(4, 10), 'The orcs have been pushed back to the caverns.');
	FFont.TextOut(makeP2D(4, 20), 'But a few loyal soldiers made it their goal to kill every last orc.');
	
	FFont.TextOut(makeP2D(4, 50), 'Now it is your turn to decide their destiny');
	FFont.TextOut(makeP2D(4, 70), 'Player 1 - Orcs');
	FFont.TextOut(makeP2D(4, 80), 'Player 2 - Humans');
	
	FFont.TextOut(makeP2D(0, 0), 'Click or press SPACE to proceed.', avButtom, ahCenter);
  end;
  
  procedure DrawMainMenu;
  begin
    MainMenuButton[0].Draw;
   MainMenuButton[1].Draw;
  MainMenuButton[2].Draw;
  
   if MainMenuButton[0].OnClick then
   begin
     Reset;
	 GameState := gsGame;
   end;
   if MainMenuButton[1].OnClick then GameState := gsInstructions;
   if MainMenuButton[2].OnClick then Application.Quit;
 
    FFont.TextOut(makeP2D(0, 0), '(C) 2009 Johannes Stein, Freeze.Dev - http://www.freeze-dev.de', avButtom, ahCenter);
  end;
  
  procedure DrawGameScene;
  var i, j: Integer;
  begin
    FBackground.Draw;
	
	for i := 0 to 5 do GObject[i].Draw;
	
    for i := 1 to 2 do
    begin
	  for j := 0 to 2 do
	  begin
        PUnit[i, j].Draw;
	  end;
    end;	
	  
	DrawGUI();
  end;

  procedure DrawInstructionScene;
  begin
    FFont.TextOut(makeP2D(0, 0), 'Cavern Defense is a turn-based strategy game for two players made for Ludum Dare #15', avTop, ahCenter);
	
	FFont.TextOut(makeP2D(4, 20), 'Click on a unit: The brighter space show where this unit can move; the red rectangle the attack radius.');
	FFont.TextOut(makeP2D(4, 30), 'Left click to move or attack.');
	FFont.TextOut(makeP2D(4, 40), 'Right click to de-select the unit.');
	FFont.TextOut(makeP2D(4, 50), 'Kill all units from the other player to win the game.');
    
	FFont.TextOut(makeP2D(4, 70), 'Click on the button (second from the right) on the top menu to end this round and let your opponent play.');
  
    FFont.TextOut(makeP2D(0, 0), '(C) 2009 Johannes Stein, Freeze.Dev - http://www.freeze-dev.de', avButtom, ahCenter);
  end;
  
  procedure DrawSettingsScene;
  begin
    
  end;
  
begin
  if GameState <> gsGame then ActiveSurface.BeginScene;

  case GameState of
    gsIntro: 		DrawIntroScene;
    gsMainMenu: 	DrawMainMenu;
	gsGame: 		DrawGameScene;
	gsInstructions: DrawInstructionScene;
	gsSettings: 	DrawSettingsScene;
  end;
end;

procedure TGame.Update;
var i, j: Integer;
begin
  GameLogic;

  for i := 1 to 2 do
  begin
    for j := 0 to 2 do
	begin
	  if PUnit[i, j].OnMouseOver then if not PUnit[i, j].Dead then PUnit[i, j].ShowHitpoints;
	  
	  if PUnit[PlayerTurn, j].OnClick then
	  begin
	    {$IFNDEF DARWIN}
		  Sound[PlayerTurn, j].Play;
		{$ENDIF}
	  
	    PUnit[PlayerTurn, 0].Marked := false;
		PUnit[PlayerTurn, 1].Marked := false;
		PUnit[PlayerTurn, 2].Marked := false;
		
		PUnit[PlayerTurn, j].Marked := true;
	  end;
	  
	  
	  {if i = PlayerTurn then
	  begin
	    
	    if PUnit[i, j].OnClick then 
		begin
		  PUnit[PlayerTurn, 0].Marked := false;
		  PUnit[PlayerTurn, 1].Marked := false;
		  PUnit[PlayerTurn, 2].Marked := false;
		
		  PUnit[i, j].Marked := true;
		end;
	  end;}
	end;
  end;
  
  {for i := 0 to 2 do
  begin
    if P1Unit[i].OnMouseOver then P1Unit[i].ShowHitpoints;
	if P2Unit[i].OnMouseOver then P2Unit[i].ShowHitpoints;
	
	if P1Turn then
	begin
	  if P1Unit[i].OnClick then 
	  begin
	    P1Unit[0].Marked := false;
		P1Unit[1].Marked := false;
		P1Unit[2].Marked := false;
		
		P1Unit[i].Marked := true;
	  end;
	end else
	begin
	  if P2Unit[i].OnClick then 
	  begin
	    P2Unit[0].Marked := false;
		P2Unit[1].Marked := false;
		P2Unit[2].Marked := false;
		
		P2Unit[i].Marked := true;
	  end;
	end;
  end;}


  // User Inputs
  with Input do
    begin
      with Keyboard do
      begin
        if isKeyUp(K_ESCAPE) then if GameState = gsMainMenu then Application.Quit else GameState := gsMainMenu;

        if isKeyUp(K_W) then ActiveSurface.ToggleFullscreen; 
		if isKeyUp(K_D) or isKeyUp(K_F) then ShowInfo := not ShowInfo;
		if (isKeyDown(KMOD_LALT) and isKeyDown(K_RETURN)) then ActiveSurface.ToggleFullscreen;
		
		if GameState = gsGame then if isKeyUp(K_R) then Reset;
		if GameState = gsIntro then if isKeyDown(K_SPACE) then GameState := gsMainMenu;
	  end;
	  
      with Mouse do
      begin
        if LeftClick then 
		begin
		
		  case GameState of
		  gsIntro: GameState := gsMainMenu;
		  gsGame:
		  begin
		  if ActiveSurface.Cursor.Y >= TOP_MENU then
		  begin
		    
			for j := 0 to 2 do
			begin
			  if PUnit[PlayerTurn, j].Marked then
			  begin
			    if PUnit[InvPlayerTurn, j].OnClick then
				begin
				  if PUnit[PlayerTurn, j].AttackPoints > 0 then
				  begin
				    if OnPoint(ActiveSurface.Cursor, PUnit[PlayerTurn, j].GetAttackRect) then
				      PUnit[PlayerTurn, j].AttackUnit(PUnit[InvPlayerTurn, j]);
				  end;
				end else
			    begin
				  // If not attack, then move
			      if PUnit[PlayerTurn, j].MovePoints > 0 then
				  begin
				    
				      if OnPoint(ActiveSurface.Cursor, PUnit[PlayerTurn, j].GetMoveRect) then 
				        PUnit[PlayerTurn, j].MoveTo(makeP2D((ActiveSurface.Cursor.X div GRID_SIZE) * GRID_SIZE, ((ActiveSurface.Cursor.Y - TOP_MENU) div GRID_SIZE) * GRID_SIZE + TOP_MENU));

				  end;
				end;
				
			  end;
			end;
			
			end;
			end;
			
			{for i := 0 to 2 do
		  	begin
			  // Player 1 units
			  if P1Unit[i].Marked then
			  begin
			    // Move Command
			    if P1Unit[i].MovePoints > 0 then 
				  P1Unit[i].MoveTo(makeP2D((ActiveSurface.Cursor.X div GRID_SIZE) * GRID_SIZE, ((ActiveSurface.Cursor.Y - TOP_MENU) div GRID_SIZE) * GRID_SIZE + TOP_MENU));
				  
				  
				// Attack Command
				if P2Unit[i].OnClick then if P1Unit[i].AttackPoints > 0 then if OnPoint(P2Unit[i].Position, ) then P1Unit[i].AttackUnit(P2Unit[i]);
			  end;
			  
			  // Player 2 units
			  if P2Unit[i].Marked then
			  begin
			    // Move Command
			    if P2Unit[i].MovePoints > 0 then 
				  P2Unit[i].MoveTo(makeP2D((ActiveSurface.Cursor.X div GRID_SIZE) * GRID_SIZE, ((ActiveSurface.Cursor.Y - TOP_MENU) div GRID_SIZE) * GRID_SIZE + TOP_MENU));
			  
			    // Attack Command
			    if P1Unit[i].OnClick then if P2Unit[i].AttackPoints > 0 then P2Unit[i].AttackUnit(P1Unit[i]);
			  end;
			end;}
			
		  end;
		end;
		
		if RightClick then
		begin
		  // Delete markers
		  for j := 0 to 2 do PUnit[PlayerTurn, j].Marked := false;
		end;
		
	  end;
		
    end;

end;

end.