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

Spieler Objekt das viele der Spielteile zusammenführt (feld und block)

Version
0.1 : 27.12  erste version
}
interface

uses yatc_field,yatc_block,JvThreadTimer,GLObjects, GLTexture,yatc_blocks
     ,yatc_global, Forms,SysUtils, Math,Classes;

type TPlayer = class
               private
                 timeout : boolean; //Timer Unschalter
               public
                 field : TBlockField;  //Spielfeld
                 preview : TBlockField; //Vorschau Feld
                 lines : integer;  //abgebaute Zeilen
                 level : integer;  //Level des Spielers
                 points : integer;  //Punkte
                 nextcube : TTCube;  //nächster & aktueller Würfel
                 cube : TTcube;
                 nextcubenumber : integer;
                 timer : TJvThreadTimer; //Timer für ablauf
                 OnPoints : TNotifyEvent;  //event wird nach UpdatePoints aufgerufen
                 OnGameOver : TNotifyEvent; //wird aufgerufen wenn Endbedinung eingetreten ist timer wird dabei gestoppt

                 //erzeugt Spieler mainCube,previewCube ziele in den anzeige erzeugt wird
                 //x,y größe Spielfeld x2,y2 größe Vorschau Feld
                 constructor Create(mainCube,previewCube : TGLDummyCube;x,y,x2,y2 : integer);
                 destructor Destroy; override;
                 procedure CreateNewBlock;  //Erzeugen einen Neuen Block im Feld bzw. Vorschau
                 procedure start;     //Starte ein Spielchen ;)
                 procedure checklines;  //Überprüft Zeilen und baut sie ab und etc.
                 procedure GameTick(Sender: TObject);  //timer Event
                 procedure UpdatePoints;  //Aktuallisiert alles was aus Punkten resultiert
                 procedure PerformAction(action : RKeyAction); //führt die Aktionen kleft bis krotleft aus
                 procedure Reset; //Setzt alles auf Startwerte zurück
               end;

implementation

uses yatc_main,yatc_gameoptions,yatc_debug,yatc_score,yatc_script;

Constructor TPlayer.Create(mainCube,previewCube : TGLDummyCube;x,y,x2,y2 : integer);

var m : TGLMaterial;

begin
  inherited Create;
  lines := 0;   //zähler setzen
  points := 0;
  level := 1;
  nextcubenumber := -1;
  nextcube := nil;
  timeout := false;

  field := TBlockField(mainCube.AddNewChild(TBlockField));
  field.setsize(x,y);

  //Standard Farben einstellen
  m := TGLMaterial.create(form1);
  m.frontProperties.Emission.Blue := 1;
  field.setMainMaterial(m);
  m.frontProperties.Emission.Blue := 0.5;
  field.setShadedMaterial(m);
  m.FrontProperties.Emission.Green := 0.7;
  m.FrontProperties.Emission.Red := 0.3;
  field.setBackgroundMaterial(m);

  preview := TBlockField(previewCube.AddNewChild(TBlockField));
  preview.setSize(x2,y2);
  preview.dummyContainer.Visible := false;
  preview.background.Visible := false;
  m.frontProperties.Emission.Blue := 1;
  preview.setMainMaterial(m);
  m.frontProperties.Emission.Blue := 0.5;
  preview.setShadedMaterial(m);
  m.FrontProperties.Emission.Green := 0.7;
  m.FrontProperties.Emission.Red := 0.3;
  preview.setBackgroundMaterial(m);
  preview.camera.Position.Z := 30;

  timer := TJvThreadTimer.create(form1);
  timer.OnTimer := GameTick;

  m.free;  //brauchen wir nur zum übernehmen
end;

destructor TPlayer.Destroy;

begin
  field.Free;
  preview.Free;
  timer.Free;
  inherited Destroy;
end;

procedure Tplayer.CreateNewBlock;

begin
  if nextcubenumber <> -1 then //nächster block vorhanden?
  begin
    cube := createblock(field,optionForm.mode,nextcubenumber);
    cube.setMaterial(nextcube.getMaterial);
  end
  else
    cube := createblock(field,optionForm.mode);
  if optionForm.blockpreview then
  begin        //Blockvorschau aktiviert?
    nextcubenumber := selectblock(optionForm.mode);
    preview.clear;
    nextcube := createblock(preview,optionForm.mode,nextcubenumber);
  end;
end;

procedure TPlayer.start;

begin
  reset;
  timer.Enabled := true;
  CreateNewBlock;
end;

procedure TPlayer.gameTick(Sender: TObject);
//Haupteil des Ablaufs, läßt die Blöcke Fallen, ruft Prüfungsfunktionen auf

var i,j,j2 : integer;

begin
  if timeout = true then timeout := false  //Abbruch des 2ten Timer Events
  else
  begin   //sonst normaler gametick

  if cube <> nil then  //schau ob cube am steuer
    if cube.isFree(ddown) then //wenn frei
      cube.domove(ddown)   //eins runter
    else
    begin              //ansonst
      cube := nil;       //cube festsetzen
      checklines;        //zeilen überprüfen
      if not (form1.gamestate = dend) then
      begin    //neuen cube erstellen
        createNewBlock;
      end;
    end;

  //Debug zeigt Feld in Tabelle an
  if form1.menudebugOn.Checked = true then
  begin
    for i := 0 to high(field.cubearray) do
    begin
      j2 := 0;
      for j := high(field.cubearray[i]) downto 0 do
      begin
        if field.cubearray[i,j] <> nil then
          debugForm.grid.Cells[i,j2] := 'x'
        else
          debugForm.grid.Cells[i,j2] := '';
        inc(j2);
      end;
    end;
  end;

  end;
end;

procedure TPlayer.checklines;  //überprüfen ob zeile voll & abbau

var i,j : integer;        //zähler
    linetoclear : boolean;    //was gefunden?
    linescleared : integer;   //anzahl gefundener zeilen


  procedure clearline(line : integer);  //Löscht eine einzelne Zeile

  var fxhelper : TLineFX;
      oldInterval : integer;   //zwischenspreicher

  begin
    fxhelper := field.createlineFX(line,form1.glcadencer1); //effekt erzeugen
    oldInterval := timer.interval;   //alte zeit sichern
    timer.Interval := 300;    //zeit die effekt angezeigt wird
    timeout := true;
    form1.glcadencer1.Enabled := true;  //das ding ist nur aktiv wenn wir es auch brauchen
    repeat                          //warten bis effekt zu ende ist
      application.ProcessMessages;
      form1.glcadencer1.Progress;
    until timeout = false;
    form1.glcadencer1.Enabled := false;
    timer.Interval := oldInterval;  //fallen wieder einstellen
    field.destroylineFx(fxhelper);

    field.clearline(line);  //Zeile löschen
    field.colapsefield;      //Feld zusammenrücken
  end;

begin         //checklines Main
  linescleared := 0;
  repeat                 //schleife zum zeilen suchen
    linetoclear := false;     //solange bis keine mehr gefunden ist
    for j := 0 to high(field.cubearray[0]) do  //zeile für zeile
    begin
      if field.checklinefull(j) then
      begin
        linetoclear := true;
        clearline(j);      //zeile löschen
        inc(linescleared);  //zeilen Zähler
        break; //for schleife abbrechen
      end;
    end;
  until linetoclear = false;

  if field.checklinefree(field.sizeY) = false then //Game Over bedingung
  begin
    timer.Enabled := false;          //Spiel beenden
    nextcubenumber := -1;  //Vorschau Löschen
    preview.clear;
    nextcube := nil;
    if @OnGameOver <> nil then OnGameOver(self);
  end;

  if linescleared > 0 then
  begin                   //punktevergabe
    lines := lines + linescleared;     //Abgebaute Zeilen
    i := points_script(linescleared);
    if i = -1 then    // - 1 : kein script gefunden
      points := points + (((linescleared*linescleared)*(field.SizeX+1))+level) //Punkte berechen
    else points := points +i;
    updatepoints;  //Anzeige erneuern
  end;
end;

procedure TPlayer.UpdatePoints;

var help : integer;

begin
  if lines > 1 then      //0 = level 1
  begin
    help :=level_script;
    if help = -1 then level := trunc(power(lines,4/6))  // level formel
    else level := help;
  end
  else level := 1;
  //test script
  help := speed_script;  //skript funktion rufen
  if help = -1 then  help := trunc((2.493*level*level)+(-4.986*level)+2.493);  //wenn kein ergebniss dann normal
                              //formel : level 20 => 900 (max)
  if basetime-help > 100 then     //zeitveringerung, nicht unter 50
  begin
    timer.Interval := basetime - help;  //falltimer
  end
  else timer.Interval := 100;
  if @OnPoints <> nil then OnPoints(self);
end;

procedure TPlayer.PerformAction(action : RKeyAction);

begin
  if timer.Enabled then
  begin
  case action of
    kleft : begin    //Links
                if cube <> nil then
                  if cube.isFree(dleft) then
                    cube.domove(dleft);
             end;
    kright : begin     //Rechts
                if cube <> nil then
                  if cube.isFree(dright) then
                    cube.domove(dright);
              end;
    kup :  begin     //Hoch
                if cube <> nil then
                  if cube.isFree(dup) then
                    begin
                      cube.domove(dup);
                      score_form.cheat := true;  //cheat melden
                    end;
              end;
    kdown :  begin    //Runter
                if cube <> nil then
                  if cube.isFree(ddown) then
                    cube.domove(ddown);
              end;
    krotright : begin     //Drehen Rechts
                if cube <> nil then
                  if cube.isrotate(drotright) then
                    cube.dorotate(drotright);
              end;
    krotleft : begin     //Drehen   Links
                if cube <> nil then
                  if cube.isrotate(drotleft) then
                    cube.dorotate(drotleft);
              end;
    kfulldown : begin      //Sofort nach unten
                if cube <> nil then
                  while cube.isfree(ddown) do
                  begin
                    cube.domove(ddown);
                  end;
                  checklines;   //direkt cube freigeben nicht auf timer warten
                  cube := nil;
                  if not (form1.gamestate = dend) then
                  begin
                    createNewBlock;
                  end;
              end;
  end;
  end;
end;

procedure TPlayer.Reset;

begin
  timer.Enabled := false;
  lines := 0;   //zähler setzen
  points := 0;
  level := 1;
  updatepoints;
  cube := nil;
  nextcube := nil;
  nextcubenumber := -1;
  field.clear;
  preview.clear;
end;

end.
