unit yatc_main;
{YATC - Unit Main
Autor : Tobias Mark aka Colenzo (tobiasmark@gmx.de)

Hauptformular mit Benutzerinteface und Userinteraktion
hällt das ganze gewissermaßen "zusammen"

Version
0.1 : 17.7 -Erste version
0.2 : 11.8 -Punkte System, Autmoatisches Fallen und Block erzeugen
0.3 : 26.8 -Thor Effekt, Panel für Steuerelmente
0.4 : 6.9 - Skript funktionen weithestgend vorhanden
          - "Debug Bereich"
0.5 : 8.9 - funktion zum laden der skripts aus selben verzeichniss
          - steuerungs erweitert, zusätzliche tasten info fenster
0.6 : 12.9 - interaktion mit highscore fenster, f10 für debug modus
0.7 : 18.9 - kleine Änderung für Mindestgröße der Schwierigkeitsgrade
           - debug modus erweiter : timer stop + aufwärts bewegen dafür dann keine Highscore
0.8 : 4.12 - vorschau funktion
           - menü
           - auslagerung der einstellungen
0.9 : 21.12 - tastenbelegung durch user konfigurierbar
0.10 : 27.12 - auslagerung des Spielers in yatc_player, nur noch interaktion mit dem Objekt
0.11 : 29.12.05 - grundlagen gelegt für Lokales 2 Spieler
0.12 : 17.3.06 - 2 Spieler modus funktioniert weitgehenden
               - Spielmodus "Synchronisiertes Abstürzen"
}

{TODO :
- Speichern von Einstellen & Tasten in xml file
- 2 Spieler Modus (neue Objekte : Spielerliste, Spieler)

 }

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, GLScene, GLObjects, GLMisc, GLWin32Viewer, GLCadencer,
  ExtCtrls, Grids,
  yatc_Block,yatc_global,uPSUtils, uPSComponent,uPSRuntime, Mask, JvExMask,
  JvSpin,Math,GLThorFX, uPSComponent_Default,yatc_script,yatc_score
  ,Keyboard, Menus, yatc_field, GLTexture, yatc_player;


type
  TForm1 = class(TForm)
    GLScene1: TGLScene;
    GLCadencer1: TGLCadencer;
    mainView: TGLSceneViewer;
    level_label: TLabel;
    scriptengine: TPSScript;
    kamera_edit: TJvSpinEdit;
    Label2: TLabel;
    new_button: TButton;
    lines_label: TLabel;
    point_label: TLabel;
    Label1: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    state_button: TButton;
    status_label: TLabel;
    control_panel: TPanel;
    PSImport_Classes1: TPSImport_Classes;
    Label9: TLabel;
    menu: TMainMenu;
    mainMenu: TMenuItem;
    menuNew: TMenuItem;
    startStopMenu: TMenuItem;
    menuQuit: TMenuItem;
    menuSettings: TMenuItem;
    menuGameSettings: TMenuItem;
    menuControlSettings: TMenuItem;
    menuHelp: TMenuItem;
    menuGameMode: TMenuItem;
    menuEsay: TMenuItem;
    menuMedium: TMenuItem;
    menuHard: TMenuItem;
    menuHigh: TMenuItem;
    menuBreak1: TMenuItem;
    menuDebug: TMenuItem;
    menuDebugOn: TMenuItem;
    menuDebugOff: TMenuItem;
    helpCube: TGLDummyCube;
    previewCube: TGLDummyCube;
    preViewView: TGLSceneViewer;
    Label5: TLabel;
    script_label: TLabel;
    mode_label: TLabel;
    size_label: TLabel;
    helpEdit: TEdit;
    secondPlayerPanel: TPanel;
    firstPlayerPanel: TPanel;
    secondMainView: TGLSceneViewer;
    player2Main: TGLDummyCube;
    player2Preview: TGLDummyCube;
    secondControlPanel: TPanel;
    secondPreViewView: TGLSceneViewer;
    second_kamera_edit: TJvSpinEdit;
    Label6: TLabel;
    Label7: TLabel;
    second_point_label: TLabel;
    Label10: TLabel;
    second_lines_label: TLabel;
    Label12: TLabel;
    second_level_label: TLabel;
    Label14: TLabel;
    menuMultiPlayer: TMenuItem;
    menuSinglePlayer: TMenuItem;
    menuTwoPlayer: TMenuItem;
    Label8: TLabel;
    multi_label: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure scriptengineCompile(Sender: TPSScript);
    procedure scriptengineExecute(Sender: TPSScript);
    procedure kamera_editChange(Sender: TObject);
    procedure new_buttonClick(Sender: TObject);
    procedure state_buttonClick(Sender: TObject);
    procedure quit_buttonClick(Sender: TObject);
    procedure help_buttonClick(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure score_buttonClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure menuEsayClick(Sender: TObject);
    procedure menuMediumClick(Sender: TObject);
    procedure menuHardClick(Sender: TObject);
    procedure menuDebugOnClick(Sender: TObject);
    procedure menuDebugOffClick(Sender: TObject);
    procedure menuGameSettingsClick(Sender: TObject);
    procedure menuControlSettingsClick(Sender: TObject);
    procedure second_kamera_editChange(Sender: TObject);
    procedure menuSinglePlayerClick(Sender: TObject);
    procedure menuTwoPlayerClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    gamestate : Rgamestate;   //Spiel zustand
    playerMode : RPlayerMode; //Anzahl der Spieler
    player1 : TPlayer;
    player2 : TPlayer; //2ter Spieler
    procedure UpdatePointInfo(Sender : Tobject);  // OnUpdate event von player1
    procedure GameOverInfo(Sender : Tobject);    // OnGameOver event von player1
    procedure FormCreateAfter(Sender : Tobject);
  end;

var
  Form1: TForm1;



implementation


uses yatc_Blocks, yatc_help, yatc_Debug, yatc_gameoptions, yatc_control;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);

begin
  //Init
  gamestate := dend;
  randomize;
  //Spieler erstellen
  player1 := TPlayer.create(helpCube,previewCube,11,14,3,3);
  player1.onPoints := UpdatePointInfo;
  player1.OnGameOver := GameOverInfo;
  //Spieler 2
  player2 := TPlayer.create(player2Main,player2Preview,11,14,3,3);
  player2.onPoints := UpdatePointInfo;
  player2.OnGameOver := GameOverInfo;

  //-> formcreateafter
 { if (optionform.feldx_edit.Value = 12) and (optionform.feldy_edit.Value = 15) then
  begin
    kamera_edit.value := 15;           //Besere Kamera als die Berechene für Startaufstellung
    second_kamera_edit.value := 15;
    player1.field.camera.position.z := 15;
    player2.field.camera.Position.Z := 15;
  end;  }


  //Neues Spiel starten   -> formcreateafter
  //new_buttonClick(sender);

  //Kameras setzen
  mainView.Camera := player1.field.camera;
  preViewView.Camera := player1.preview.camera;

  secondMainView.Camera := player2.field.camera;
  secondPreViewView.Camera := player2.preview.camera;

  //Punkte aktuallisieren   ->formcreateafter
  //optionForm.UpdateStatus;

  //Mehspieler Modus : 1 Spieler
  secondPlayerPanel.Visible := false;
  form1.Width := 650;
  playerMode := ksingle;
  menuSinglePlayer.Checked := true;
end;

procedure Tform1.formcreateafter(sender : Tobject);
//wird vom Options form aufgerufen nachdem selbiges geladen ist
//da wir eigentlich noch net fertig  sind mit init
begin
  //Besere Kamera als die Berechene für Standartgröße
  if (optionform.feldx_edit.Value = 12) and (optionform.feldy_edit.Value = 15) then
  begin
    kamera_edit.value := 15;
    second_kamera_edit.value := 15;
    player1.field.camera.position.z := 15;
    player2.field.camera.Position.Z := 15;
  end;
  //Neues Spiel starten
  new_buttonClick(sender);
  //Punkte aktuallisieren
  optionForm.UpdateStatus;
end;

procedure TForm1.UpdatePointInfo(Sender : Tobject);

begin
  if (sender = player1) then
  begin
    point_label.caption := inttostr(player1.points);  //Anzeige aktuallisieren
    lines_label.caption := inttostr(player1.lines);
    level_label.caption := inttostr(player1.level);
  end
  else if (sender = player2) then
  begin
    second_point_label.caption := inttostr(player2.points);  //Anzeige aktuallisieren
    second_lines_label.caption := inttostr(player2.lines);
    second_level_label.caption := inttostr(player2.level);
  end;
end;

procedure TForm1.GameOverInfo(Sender : Tobject);

begin  //Spiel für beendet erklären
  if (PlayerMode = ktwo) then begin
    if player1.timer.Enabled or player2.timer.Enabled then
    begin
      //Status meldung das einer der beiden zuende hat;)
      if (sender = player1) then
        status_label.Caption := 'Spieler 1 Game Over'
      else
        status_label.Caption := 'Spieler 2 Game Over';
      exit; // abbrechen falls noch einer der beiden läuft
    end;
  end;
  gamestate := dend;
  status_label.Caption := 'Game Over';
  case PlayerMode of
    ksingle : begin
      showmessage('Game Over!');
      score_form.add(optionForm.mode,player1.points,player1.field.sizeX+1,player1.field.SizeY+1,optionForm.script_select.Items[optionForm.script_select.itemindex]); //bestenliste
    end;
    ktwo : begin
      if player1.points > player2.points then
        Showmessage('Game Over - Spieler 1 gewinnt')
      else
        if player1.points < player2.points then
           Showmessage('Game Over - Spieler 2 gewinnt')
        else
           Showmessage('Game Over - Gleischstand');
      score_form.add(optionForm.mode,player1.points,player1.field.sizeX+1,player1.field.SizeY+1,optionForm.script_select.Items[optionForm.script_select.itemindex],'Spieler 1'); //bestenliste
      score_form.add(optionForm.mode,player2.points,player2.field.sizeX+1,player2.field.SizeY+1,optionForm.script_select.Items[optionForm.script_select.itemindex],'Spieler 2'); //bestenliste
    end;
  end;
  state_button.Enabled := false;
  menuSettings.Enabled := true;
  menuGameMode.Enabled := true;
  menuMultiPlayer.Enabled := true;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
//Benutzer interface, fragt tastebelung bei controlForm ab und führt dann die
//passende aktion aus

begin
  case controlform.getkeyaction(key) of  //aktion herausfinden
    kleft : begin    //Links
                if gamestate = drun then
                  player1.PerformAction(kleft);
             end;
    kleft2 : begin    //Links
                if gamestate = drun then
                  if PlayerMode = ktwo then
                     player2.PerformAction(kleft)
                  else
                     player1.PerformAction(kleft);
             end;
    kright : begin     //Rechts
                if gamestate = drun then
                  player1.PerformAction(kright);
              end;
    kright2 : begin     //Rechts
                if gamestate = drun then
                   if PlayerMode = ktwo then
                     player2.PerformAction(kright)
                  else
                     player1.PerformAction(kright);
              end;
    kup :  begin     //Hoch
                if menudebugOn.checked = true then //nur im debug modus verwenden
                  if gamestate = drun then
                    player1.PerformAction(kup);
              end;
    kup2 :  begin     //Hoch
                if menudebugOn.checked = true then //nur im debug modus verwenden
                  if gamestate = drun then
                     if PlayerMode = ktwo then
                       player2.PerformAction(kup)
                    else
                       player1.PerformAction(kup);
              end;
    kdown :  begin    //Runter
                if gamestate = drun then
                  player1.PerformAction(kdown);
              end;
    kdown2 :  begin    //Runter
                if gamestate = drun then
                   if PlayerMode = ktwo then
                     player2.PerformAction(kdown)
                  else
                     player1.PerformAction(kdown);
              end;
    krotright : begin     //Drehen Rechts
                if gamestate = drun then
                  player1.PerformAction(krotright);
              end;
    krotright2 : begin     //Drehen Rechts
                if gamestate = drun then
                  if PlayerMode = ktwo then
                     player2.PerformAction(krotright)
                  else
                     player1.PerformAction(krotright);
              end;
    krotleft : begin     //Drehen   Links
                if gamestate = drun then
                  player1.PerformAction(krotleft);
              end;
    krotleft2 : begin     //Drehen   Links
                if gamestate = drun then
                   if PlayerMode = ktwo then
                     player2.PerformAction(krotleft)
                  else
                     player1.PerformAction(krotleft);
              end;
    kfulldown : begin      //Sofort nach unten
                if gamestate = drun then
                begin
                  player1.PerformAction(kfulldown);
                  if (optionform.syncfulldown and (PlayerMode = ktwo)) then
                    player2.PerformAction(kfulldown); // 2t spieler mitziehen
                end;
              end;
    kfulldown2 : begin      //Sofort nach unten
                if gamestate = drun then
                   if PlayerMode = ktwo then
                   begin
                     player2.PerformAction(kfulldown);
                     if (optionform.syncfulldown) then
                       player1.PerformAction(kfulldown);
                   end
                   else
                     player1.PerformAction(kfulldown);
              end;
    kcameraUp : begin
                 kamera_edit.Value := kamera_edit.Value +1;
               end;
    kcameraUp2 : begin
                 second_kamera_edit.Value := second_kamera_edit.Value +1;
               end;
    kcameraDown : begin
                  kamera_edit.Value := kamera_edit.Value -1;
                end;
    kcameraDown2 : begin
                  second_kamera_edit.Value := second_kamera_edit.Value -1;
                end;
    knew :  new_buttonclick(sender);
    kstart : state_buttonclick(sender);
    khelp : help_buttonclick(sender);
    khighscore : score_buttonclick(sender);
    kdebug :  begin
                 if debugForm.Visible = false then
                   menuDebugOnClick(sender)
                 else
                   menuDebugOffClick(sender);
               end;

  end;
  key := 0;
end;

procedure TForm1.kamera_editChange(Sender: TObject);

begin
  player1.field.camera.Position.z := trunc(kamera_edit.Value);     //Kamera Abstand ändern
end;

procedure TForm1.new_buttonClick(Sender: TObject);   //Starte ein Neues Spiel (im Gestopten zustand)

begin
  if (player1.points > 0) and (gamestate <> dend) then score_form.add(optionForm.mode,player1.points,player1.field.SizeX+1,player1.field.SizeY+1,optionForm.script_select.Items[optionForm.script_select.itemindex],'Spieler 1'); //wenn neu klick dann auch bestenliste
  player1.Reset;
  if (player2.points > 0) and (gamestate <> dend) then score_form.add(optionForm.mode,player1.points,player2.field.SizeX+1,player2.field.SizeY+1,optionForm.script_select.Items[optionForm.script_select.itemindex],'Spieler 2'); //wenn neu klick dann auch bestenliste
  player2.Reset;
  gamestate := dend;
  status_label.Caption := 'Gestopt';  //Anzeige einstellen
  state_button.Caption := 'Start (F3)';
  state_button.Enabled := true;        //Buttons aktivieren
  menuSettings.Enabled := true;
  menuGameMode.Enabled := true;
  menuMultiPlayer.Enabled := true;
  newgame_script;  //init szenario
end;

procedure TForm1.state_buttonClick(Sender: TObject); //Zustands Wechseln

begin
  case gamestate of
    dend : begin      //Spiel Starten
             status_label.Caption := 'Gestartet';
             state_button.Caption := 'Pause';
             menuGameMode.Enabled := false;
             menuSettings.Enabled := false;
             menuMultiPlayer.enabled := false;
             optionForm.UpdateStatus;
             case PlayerMode of
               ksingle : begin
                 player1.start; //Starten des Spielers!
               end;
               ktwo : begin    //Beide Spieler starten
                 player1.start;
                 player2.start;
               end;
             end;
             gamestate := drun;  //gestartet!
             helpEdit.SetFocus;  //EingabeFokus auf edit das durch tastedrücke nichts wildes passiert
           end;
    drun : begin     //Spiel Pausen
             case PlayerMode of
               ksingle : begin
                 player1.timer.Enabled := false;
               end;
               ktwo : begin
                 player1.timer.Enabled := false;
                 player2.timer.Enabled := false;
               end;
             end;
             status_label.Caption := 'Gestopt';
             state_button.Caption := 'Start';
             menuSettings.Enabled := true;
             gamestate := dstoped;
           end;
    dstoped : begin    //Spiel Wiederaufnehmen
              case PlayerMode of
               ksingle : begin
                 player1.timer.Enabled := true;
               end;
               ktwo : begin
                 player1.timer.Enabled := true;
                 player2.timer.Enabled := true;
               end;
             end;
             status_label.Caption := 'Gestartet';
             state_button.Caption := 'Pause';
             menuSettings.Enabled := false;
             gamestate := drun;
             helpEdit.SetFocus;
           end;
  end;
end;

procedure TForm1.scriptengineCompile(Sender: TPSScript);

begin     //Registieren von Funktionen & Variablen
  scriptengine.AddFunction(@newblock_script,'function newblock(x,y : integer;b,g,r: single):Tobject');
  scriptengine.AddFunction(@setresult_script,'procedure setresult(block : Tobject)');
  scriptengine.addfunction(@connectright_script,'procedure connectright(block1,block2 : Tobject)');
  scriptengine.addfunction(@connectleft_script,'procedure connecteft(block1,block2 : Tobject)');
  scriptengine.addfunction(@connectup_script,'procedure connectup(block1,block2 : Tobject)');
  scriptengine.addfunction(@connectdown_script,'procedure connectdown(block1,block2 : Tobject)');
  scriptengine.addfunction(@setcamera_script,'procedure setcamera(position : integer)');
  scriptengine.addfunction(@updatedata_script,'procedure updatedata');
  scriptengine.addfunction(@lockmode_script,'procedure lockmode');
  scriptengine.addfunction(@locksize_script,'procedure locksize');
  scriptengine.addfunction(@randomcolor_script,'function randomcolor : single');
  scriptengine.addfunction(@randomint_script,'function randomint(range : integer) : integer');
  scriptengine.addfunction(@dialog_script,'procedure dialog(text : string)');
  scriptengine.addfunction(@setSize_script,'procedure setSize(x,y : integer)');
  scriptengine.addfunction(@setpreviewSize_script,'procedure setpreviewSize(x,y : integer)');
  scriptengine.AddRegisteredPTRVariable('feldx', 'Integer');
  scriptengine.AddRegisteredPTRVariable('feldy', 'Integer');
  scriptengine.AddRegisteredPTRVariable('mode', 'byte');
  scriptengine.AddRegisteredPTRVariable('level', 'Integer');
  scriptengine.AddRegisteredPTRVariable('cubecount', 'Integer');
  scriptengine.AddRegisteredPTRVariable('lines', 'Integer');
  scriptengine.AddRegisteredPTRVariable('points', 'Integer');
  scriptengine.AddRegisteredPTRVariable('speed', 'Integer');
  scriptengine.AddRegisteredPTRVariable('blockpreview', 'boolean');
end;

procedure TForm1.scriptengineExecute(Sender: TPSScript);


begin  //Variablen Zuweisen
  scriptengine.SetPointerToData('mode', @optionForm.mode, scriptengine.FindBaseType(btu8));
  scriptengine.SetPointerToData('level', @player1.level, scriptengine.FindBaseType(bts32));
  scriptengine.SetPointerToData('feldx', @player1.field.sizeX, scriptengine.FindBaseType(bts32));
  scriptengine.SetPointerToData('feldy', @player1.field.sizeY, scriptengine.FindBaseType(bts32));
  scriptengine.SetPointerToData('lines', @player1.lines, scriptengine.FindBaseType(bts32));
  scriptengine.SetPointerToData('points', @player1.points, scriptengine.FindBaseType(bts32));
  scriptengine.SetPointerToData('speed', @player1.timer.interval, scriptengine.FindBaseType(bts32));
  scriptengine.SetPointerToData('cubecount', @optionForm.cubecount, scriptengine.FindBaseType(bts32));
  scriptengine.SetPointerToData('blockpreview', @optionForm.blockpreview, scriptengine.FindBaseType(btu8));
end;

procedure TForm1.quit_buttonClick(Sender: TObject);

begin
  application.Terminate;
end;

procedure TForm1.help_buttonClick(Sender: TObject);

begin
  help_form.visible := true;
end;

procedure TForm1.score_buttonClick(Sender: TObject);

begin
  score_form.visible := true;
  score_form.setfocus;
end;

procedure TForm1.FormDestroy(Sender: TObject);

begin
  player1.Free;
  player2.Free;
end;

procedure TForm1.menuEsayClick(Sender: TObject);

begin
  menuEsay.checked := true;
  optionForm.mode := desay;
  player1.preview.setSize(3,3);
  player2.preview.setSize(3,3);
end;

procedure TForm1.menuMediumClick(Sender: TObject);

begin
  menuMedium.checked := true;
  optionForm.mode := dmedium;
  player1.preview.setSize(4,4);
  if player1.field.sizeX < 8 then player1.field.setSize(8,player1.field.sizeY);
  if player1.field.sizeY < 8 then player1.field.setSize(player1.field.sizeX,8);
  player2.preview.setSize(4,4);
  if player2.field.sizeX < 8 then player2.field.setSize(8,player2.field.sizeY);
  if player2.field.sizeY < 8 then player2.field.setSize(player2.field.sizeX,8);
end;

procedure TForm1.menuHardClick(Sender: TObject);

begin
  menuHard.checked := true;
  optionForm.mode := dhard;
  player1.preview.setSize(5,5);
  if player1.field.sizeX < 12 then player1.field.setSize(8,player1.field.sizeY);
  if player1.field.sizeY < 12 then player1.field.setSize(player1.field.sizeX,8);
  player2.preview.setSize(5,5);
  if player2.field.sizeX < 12 then player2.field.setSize(8,player2.field.sizeY);
  if player2.field.sizeY < 12 then player2.field.setSize(player2.field.sizeX,8);
end;

procedure TForm1.menuDebugOnClick(Sender: TObject);

begin
  debugForm.Visible := true;
  menudebugOn.Checked := true;
end;

procedure TForm1.menuDebugOffClick(Sender: TObject);

begin
  debugForm.Visible := false;
  menudebugOff.Checked := true;
end;

procedure TForm1.menuGameSettingsClick(Sender: TObject);

begin
  if (gamestate = dstoped) or (gamestate = dend) then
  begin
    optionForm.makevisible;
  end;
end;

procedure TForm1.menuControlSettingsClick(Sender: TObject);

begin
  if (gamestate = dstoped) or (gamestate = dend) then
  begin
    controlForm.makevisible;
  end;
end;

procedure TForm1.second_kamera_editChange(Sender: TObject);

begin
  player2.field.camera.Position.z := trunc(second_kamera_edit.Value);     //Kamera Abstand ändern
end;

procedure TForm1.menuSinglePlayerClick(Sender: TObject);

//Umstellen der anzeige

begin
  secondPlayerPanel.Visible := false;
  form1.Width := 650;
  playerMode := ksingle;
  menuSinglePlayer.Checked := true;
  optionForm.UpdateStatus;
  form1.Position := poDesktopCenter;
end;

procedure TForm1.menuTwoPlayerClick(Sender: TObject);

begin
  secondPlayerPanel.Visible := true;
  form1.Width := 1230;
  playerMode := ktwo;
  menuTwoPlayer.Checked := true;
  optionForm.UpdateStatus;
  form1.Position := poDesktopCenter;
end;

procedure TForm1.FormResize(Sender: TObject);

begin
  if playerMode = ktwo then
  begin
    secondPlayerPanel.Width := round(form1.Width / 2) ;
  end;
end;

end.
