unit logaut;  //.. рабочее название - "кручу-верчу"
              //.. для проекта "Отшельник"

{
-----  Движок для логической игры          -----
-----                                      -----
----- (c) Trofimov cdmlex Aleksey          -----
----- www.cdmlex.by.ru                     -----
}

interface
uses
  Mform, GLFile3DS, Dialogs, Otshelm_A, Otshelm_B, Otshelm_C,
  SysUtils, Graphics, GLTexture, GLScene,
  GLObjects, GLGeomObjects, Forms, ghload,
  Jpeg, OpenGL1x, GLMisc, GLfileSMD,
  loader, GLVectorFileObjects, Model,
  GLProxyObjects, GLFireFX, Setup, Sound;

type
  TLgP = record
    M : tglFreeFormProxy;
    C : tglCube;
    D : tglDummyCube;
    R, RR, N, Ncu : byte;
    MR : shortint;
    SA: array[1..4] of boolean;
  end;
  TlgU =record
    T : byte;
    U : boolean;
  end;
  TCu = record
    M : TglActor;
    X, Y : byte;
    S : word;
  end;

procedure InitLG;
procedure clear;

Procedure StartLogGame;
Procedure ResetLogGame;
Procedure GetSA(X,Y : byte);
Procedure addACU(X,Y: byte);
Procedure addBCU(X,Y: byte);
procedure Alg(X,Y : byte);
procedure KrOff(X,Y : byte);
procedure winLG;
procedure WorkLG;
Procedure LoadLG(FN: string);
procedure MRClick(X,Y : word);

const
  MPX = 8;
  MPY = 8;
  VintAP = 700;

var
  trm : array[1..10] of TGLFreeForm;
  GLlgCube, GlLgVis, GLLGUnVis, GLFuv  : TGLDummyCube;
  LgP : array[1..MPX,1..MPY] of TLgP;
  ACU : array[1..5] of TCu;
  BCU : array[1..5] of TCu;

  Clicks : word;

  Pole : tglPolygon;
  LgU : array[0..MPX+1,0..MPY+1] of TLgu;

  GLMlg : TGLMATERIALlIBRARY;
  GLFire: array [1..4] of TglFireFXManager;

  MofA, MofB, MofP : byte;

  ML, MR : boolean;

implementation

procedure LoadComp;
var
  I : byte;
  FN : string;
begin
  GLFuv:=TGLDummyCube(GLLgCube.AddNewChild(TGLDummyCube));
  GLFuv.Position.Z:=0.5;
  UnZ('\Data\Models\lgt.cdmlex');

  FN:=extractfilepath(Application.ExeName)+ '\Data\temp\truba.jpg';
  if FileExists(FN) then
  GLMlg.AddTextureMaterial('t', FN);

  FN:=extractfilepath(Application.ExeName)+ '\Data\temp\mc.jpg';
  if FileExists(FN) then
  GLMlg.AddTextureMaterial('mc', FN);

  FN:=extractfilepath(Application.ExeName)+ '\Data\temp\bar.jpg';
  if FileExists(FN) then
  GLMlg.AddTextureMaterial('bar', FN);

  FN:=extractfilepath(Application.ExeName)+ '\Data\temp\kran.jpg';
  if FileExists(FN) then
  GLMlg.AddTextureMaterial('kran', FN);

  FN:=extractfilepath(Application.ExeName)+ '\Data\temp\vt.3DS';
  if FileExists(FN) then
  for I:=1 to 5 do
    begin
      ACU[I].M:=TglActor(GLlgVis.AddNewChild(TglActor));
      ACU[I].M.Scale.SetVector(0.005, 0.005, 0.005);
      ACU[I].M.Material.MaterialLibrary:=GLMlg;
      ACU[I].M.Material.LibMaterialName:='kran';
      ACU[I].M.LoadFromFile(FN);
      ACU[I].M.Visible:=false;
    end;
  FN:=extractfilepath(Application.ExeName)+ '\Data\temp\mr.jpg';
  if FileExists(FN) then
  GLMlg.AddTextureMaterial('mramor', FN);

  FN:=extractfilepath(Application.ExeName)+ '\Data\temp\corob.jpg';
  if FileExists(FN) then
  GLMlg.AddTextureMaterial('corob', FN);

  FN:=extractfilepath(Application.ExeName)+ '\Data\temp\mr.3DS';
  Model.mr:=TGLFreeForm(Model.Etalon.AddNewChild(TGLFreeForm));
  if FileExists(FN) then
  Model.mr.LoadFromFile(FN);
  Model.mr.Material.MaterialLibrary:=GLMlg;
  Model.mr.Material.LibMaterialName:='mramor';

  FN:=extractfilepath(Application.ExeName)+ '\Data\temp\';

  if FileExists(FN+'bar.smd') then
  for I:=1 to 5 do
    begin
      BCU[I].M:=TglActor(GLlgVis.AddNewChild(TglActor));
      BCU[I].M.Scale.SetVector(0.011, 0.011, 0.011);
      BCU[I].M.Material.MaterialLibrary:=GLMlg;
      BCU[I].M.Material.LibMaterialName:='bar';
      BCU[I].M.LoadFromFile(FN+'bar.smd');
      SetCurrentDir(FN);
      if FileExists(FN+'bar1.smd') then BCU[I].M.AddDataFromFile('bar1.smd');
      if FileExists(FN+'bar2.smd') then BCU[I].M.AddDataFromFile('bar2.smd');
      if FileExists(FN+'bar3.smd') then BCU[I].M.AddDataFromFile('bar3.smd');
    //  BCU[I].M.Animations[1].MakeSkeletalTranslationStatic;
      BCU[I].M.SwitchToAnimation('bar1');

      BCU[I].M.AnimationMode:=aamNone;
   //   BCU[I].M.PitchAngle:=80;
      ACU[I].M.Visible:=false;
    end;


  for I:=1 to 10 do
    begin
      trm[I]:= TGLFreeForm(Etalon.AddNewChild(TGLFreeForm));
      trm[I].Material.MaterialLibrary:=GLMlg;
      trm[I].Material.LibMaterialName:='t';
    end;

  for I:=1 to 5 do
    begin
      FN:=extractfilepath(Application.ExeName)+ '\Data\temp\t' + IntToStr(i)+'.3DS';
      if FileExists(FN) then
      trm[I].LoadFromFile(FN);
      FN:=extractfilepath(Application.ExeName)+ '\Data\temp\v' + IntToStr(i)+'.3DS';
      if FileExists(FN) then
      trm[I+5].LoadFromFile(FN);
    end;
end;

procedure Clear;
var
  IX, IY : byte;
begin
  for IX:=1 to MPX do
  for IY:=1 to MPY do
    begin
      LgP[IX,IY].M.MasterObject:=nil;
      LgP[IX,IY].R:=1;
      LgP[IX,IY].RR:=1;
      LgP[IX,IY].N:=0;
      LgP[IX,IY].Ncu:=0;
      LgP[IX,IY].D.Effects.Clear;
    end;
  for IX:=1 to 5 do
    begin
      ACU[IX].X:=0;
      ACU[IX].Y:=0;
      ACU[IX].S:=0;
      BCU[IX].X:=0;
      BCU[IX].Y:=0;
      BCU[IX].S:=0;
      ACU[IX].M.Visible:=false;
      BCU[IX].M.Visible:=false;
    end;
  MofA:=0;
  MofB:=0;
  MofP:=0;
  for IX:=0 to MPX+1 do
  for IY:=0 to MPY+1 do
  begin
  Lgu[IX,IY].U:=false;
  LgU[IX,IY].T:=0;
  end;
end;

///////////////////////////////////////////////////////////////
procedure InitLg;
var
  IX, IY : byte;
begin

  GLlgCube:=TGLDummyCube(OtShelm_A.GLWork.AddNewChild(TGLDummyCube));
   Pole:=tglPolygon(GLlgCube.AddNewChild(tglPolygon));
   Pole.Nodes.AddNode( MPX div 2, MPY div 2,0);
   Pole.Nodes.AddNode(-MPX div 2, MPY div 2,0);
   Pole.Nodes.AddNode(-MPX div 2,-MPY div 2,0);
   Pole.Nodes.AddNode( MPX div 2,-MPY div 2,0);
  GLlgVis:=TGLDummyCube(GLLgCube.AddNewChild(TGLDummyCube));
  GLlgUnVis:=TGLDummyCube(GLLgCube.AddNewChild(TGLDummyCube));
  GLlgUnVis.Visible:=false;
  GLMlg := TGLMaterialLibrary.Create(OForm);
  LoadComp;

   Pole.Material.MaterialLibrary:=GLMlg;
   Pole.Material.LibMaterialName:='mc';

  for IX:=1 to MPX do
  for IY:=1 to MPY do
    begin
      LgP[IX,IY].C:=TGLCube(GLLgUnVis.AddNewChild(TGLCube));
      LgP[IX,IY].C.Scale.SetVector(0.8, 0.8, 0.8);
      LgP[IX,IY].C.Position.SetPoint(IX-(MPX div 2)-0.5,IY-(MPY div 2 )-0.5,0);
      LgP[IX,IY].C.Name:='NVL'+inttostr(1000000+Ix*1000+Iy);

      LgP[IX,IY].D:=TGLDummyCube(GLFuv.AddNewChild(TGLDummyCube));
      LgP[IX,IY].D.Scale.SetVector(1, 1, 1);
      LgP[IX,IY].D.Position.SetPoint(IX-(MPX div 2)-0.5,IY-(MPY div 2 )-0.5,0);


      LgP[IX,IY].M:=tglFreeFormProxy(GLLgVis.AddNewChild(tglFreeFormProxy));
      with LgP[IX,IY].M do
        begin
          Scale.SetVector(0.005, 0.005, 0.005);
          ProxyOptions:=[pooObjects];

          Position.SetPoint(IX-(MPX div 2)-0.5,IY-(MPY div 2 )-0.5,0);
        end;
    end;
   clear;
   for IX:=1 to 4 do
   begin
     GLFire[ix]:= TGLFireFXManager.Create(OForm);
     GLFire[ix].FireEvaporation:=0.17;
     GLFire[ix].FireRadius:=0.25;
     GLFire[ix].MaxParticles:=17;
     GLFire[ix].ParticleLife:=1;
     GLFire[ix].ParticleSize:=0.8;
     GLFire[ix].UseInterval:=false;

     GLFire[ix].FireDensity:=2;
     GLFire[ix].FireBurst:=1;
     GLFire[ix].Disabled:=false;
     GLFire[ix].Cadencer:=OForm.Cadencer;
     GLFire[ix].Paused:=false;
     GLFire[ix].InnerColor.SetColor(941 ,953 ,957 ,1);
     GLFire[ix].OuterColor.SetColor(212 ,314 ,380 ,1);
     GLFire[ix].FireInit;
   end;
     GLFire[1].InitialDir.SetPoint(-0.9,0,1.5);
     GLFire[2].InitialDir.SetPoint(0,-0.9,1.5);
     GLFire[3].InitialDir.SetPoint(-0.9,0,1.5);
     GLFire[4].InitialDir.SetPoint(0,-0.9,1.5);
     GLFire[1].FireDir.SetPoint( 3, 0,2);
     GLFire[2].FireDir.SetPoint( 0,-3,2);
     GLFire[3].FireDir.SetPoint(-3, 0,2);
     GLFire[4].FireDir.SetPoint( 0, 3,2);

  GLlgCube.Scale.SetVector(0.17,0.17,0.17);
  GLlgCube.Visible:=false;
end;


////////////////////////////////////////////////////////////////


procedure Trright(X,Y : byte);
begin
  if LGP[X,Y].R=1 then LGP[X,Y].R:=4
  else
  LGP[X,Y].R:=LGP[X,Y].R-1;
  LGP[X,Y].MR:=+2;
end;
procedure Trleft(X,Y : byte);
begin
  if LGP[X,Y].R=4 then LGP[X,Y].R:=1
  else
  LGP[X,Y].R:=LGP[X,Y].R+1;
  LGP[X,Y].MR:=-2;
end;

function rollTR(X,Y : byte): boolean;
begin
  result:=false;
  if ((LGP[X,Y].R=1) and (round(    LGP[X,Y].M.RollAngle) =0))
  or ((LGP[X,Y].R=2) and (round(    LGP[X,Y].M.RollAngle) =-90))
  or ((LGP[X,Y].R=3) and (round(abs(LGP[X,Y].M.RollAngle))=180))
  or ((LGP[X,Y].R=4) and (round(    LGP[X,Y].M.RollAngle) =90))
  then
  result:=true
  else
  LGP[X,Y].M.RollAngle:=LGP[X,Y].M.RollAngle+LGP[X,Y].MR;
end;

Procedure StartLogGame;
var
IX,IY : byte;
begin
  for IX:=1 to MPX do
  for IY:=1 to MPY do
  Lgp[IX,IY].RR:=Lgp[IX,IY].R;
end;

Procedure ResetLogGame;
var
IX,IY : byte;
begin
  for IX:=1 to MPX do
  for IY:=1 to MPY do
  begin
  Lgp[IX,IY].R:=Lgp[IX,IY].RR;
  Lgp[IX,IY].D.Effects.Clear;
  GetSA (IX,IY);
  end;
  for IX:=1 to 5 do
  ACU[IX].S:=0;
  MofP:=0;
  StartLogGame;
end;

Procedure  GetSA(X,Y : byte);
var
  K : byte;
begin
  with Lgp[X,Y] do
  begin
  K:=(N-1)*4 +R;

  if (K in [2,3,4,7,8,11,18,20]) then SA[1]:=false else SA[1]:=true;
  if (K in [1,3,4,5,8,12,17,19]) then SA[2]:=false else SA[2]:=true;
  if (K in [1,2,4,5,6,9 ,18,20]) then SA[3]:=false else SA[3]:=true;
  if (K in [1,2,3,6,7,10,17,19]) then SA[4]:=false else SA[4]:=true;
  end;
end;

Procedure addACU(X,Y: byte);
begin
  if (MofA<5) and (LgU[X,Y].T>0) then
  begin
    MofA:=MofA+1;
    ACU[MofA].M.Position:=Lgp[X,Y].C.Position;
    ACU[MofA].X:=X;
    ACU[MofA].Y:=Y;
    ACU[MofA].M.Visible:=true;
    Lgp[X,Y].Ncu:=MofA;
  end;
end;

Procedure addBCU(X,Y: byte);
begin
  if (MofB<5) and (LgU[X,Y].T>0) then
  begin
    MofB:=MofB+1;
    BCU[MofB].M.Position:=Lgp[X,Y].C.Position;
    BCU[MofB].M.Position.Z:=0.4;
    BCU[MofB].X:=X;
    BCU[MofB].Y:=Y;
    BCU[MofB].M.Visible:=true;
    Lgp[X,Y].Ncu:=MofB+5;
  end;
end;

procedure delPar(X,Y : byte);
begin
  lgP[X,Y].D.Effects.Clear;
  MofP:=MofP-1;
  if MofP>0 then
  Sound.PsoundY2('par')
  else
  GamerSound2.Behaviours.Clear;
end;

procedure KrOff(X,Y : byte);
begin
  if (LgU[X,Y].U) and (LgU[X,Y].T>0) then
    begin
      LgU[X,Y].U:=false;

      if (lgP[X,Y].Ncu>5) then
      with BCU[lgP[X,Y].Ncu-5] do
      begin
      M.SwitchToAnimation('bar3');
      M.AnimationMode:=aamPlayOnce;
      S:=0;
      end;

      if (lgP[X,Y].SA[1]) then
      if (lgU[X+1,Y].T<>0) and (lgP[X+1,Y].SA[3])  then KrOFF(X+1,Y)
      else delPar(X+1,Y);
      if (lgP[X,Y].SA[2]) then if (lgU[X,Y-1].T<>0) and (lgP[X,Y-1].SA[4]) then KROFF(X,Y-1)
      else delPar(X,Y-1);
      if (lgP[X,Y].SA[3]) then if (lgU[X-1,Y].T<>0) and (lgP[X-1,Y].SA[1]) then KrOFF(X-1,Y)
      else delPar(X-1,Y);
      if (lgP[X,Y].SA[4]) then if (lgU[X,Y+1].T<>0) and (lgP[X,Y+1].SA[2]) then KroFF(X,Y+1)
      else delPar(X,Y+1);

    end;
end;

procedure addPar(X,Y, T : byte);
begin
  GetOrCreateFireFX(lgP[X,Y].D).Manager:=GLFire[T];
  MofP:=MofP+1;
  if MofP>0 then
  Sound.PsoundY2('par');

end;

procedure Alg(X,Y : byte);
begin
  if (LgU[X,Y].U) and (LgU[X,Y].T>0) then
    begin
      LgU[X,Y].U:=false;

      if (lgP[X,Y].Ncu>5) then
      with BCU[lgP[X,Y].Ncu-5] do
      begin
      M.SwitchToAnimation('bar1');
      M.AnimationMode:=aamPlayOnce;
      S:=100;
      end;

      if (lgP[X,Y].SA[1]) then
      if (lgU[X+1,Y].T<>0) and (lgP[X+1,Y].SA[3])  then Alg(X+1,Y)
      else addPar(X+1,Y,1);
      if (lgP[X,Y].SA[2]) then if (lgU[X,Y-1].T<>0) and (lgP[X,Y-1].SA[4]) then Alg(X,Y-1)
      else addPar(X,Y-1,2);
      if (lgP[X,Y].SA[3]) then if (lgU[X-1,Y].T<>0) and (lgP[X-1,Y].SA[1]) then Alg(X-1,Y)
      else addPar(X-1,Y,3);
      if (lgP[X,Y].SA[4]) then if (lgU[X,Y+1].T<>0) and (lgP[X,Y+1].SA[2]) then Alg(X,Y+1)
      else addPar(X,Y+1,4);
    end;
end;

procedure winLG;
var
  IIB : byte;
  WLB : boolean;
begin
  WLB:=true;
  if MofP=0 then
    begin
     for IIB:=1 to MofB do
     if BCU[IIB].S<>100 then WLB:=false;
     if WLB then
     begin
        Sound.PSoundY('win');
        getguimess('Задание выполнена','Поздравляем, вы починилм трубу, теперь вы    можете нажать кнопку выйти.','Хорошо');
        Model.Corob[CorI].Br:=false;
        TCWin:=TCWin+1;
     end;
    end;
end;


procedure creatLG;
var
OX,OY : byte;
begin
   for OX:=1 to MPX do
   for OY:=1 to MPY do
   if LgU[OX,OY].T>0 then
   begin

      Lgp[OX,OY].M.MasterObject:=trm[LgU[OX,OY].T];
      if LgU[OX,OY].T<6 then
      Lgp[OX,OY].N:=LgU[OX,OY].T
      else
      Lgp[OX,OY].N:=LgU[OX,OY].T-5;
      if (LGP[OX,OY].R=1) then LGP[OX,OY].M.RollAngle:=0;
      if (LGP[OX,OY].R=2) then LGP[OX,OY].M.RollAngle:=-90;
      if (LGP[OX,OY].R=3) then LGP[OX,OY].M.RollAngle:=180;
      if (LGP[OX,OY].R=4) then LGP[OX,OY].M.RollAngle:=90;
      getSA(OX,OY);

      if (LGP[OX,OY].Ncu>0) and (LGP[OX,OY].Ncu<6) then
      AddACU(OX,OY);
      if (LGP[OX,OY].Ncu>5) then
      AddBCU(OX,OY);

   end;

end;

Procedure LoadLG(FN: string);
var
  F : textfile;
  IX, IY, I, TT : word;
  S : string;
begin
  assignfile(F,fn);
  reset(F);
  readln(F, S);
  if S='__cdmlex otshelm lg file__' then
    begin
      clear;
      for IX:=1 to MPX do
        begin
          readln(F,S);
          TT:=0;
          IY:=0;
          For I:=1 to length(S) do
          if copy(S,I,1)=',' then
            begin
              IY:=IY+1;
              LgU[IX,IY].T:=strtoint(copy(S,TT+1,I-TT-1));
              TT:=I;
            end;
          readln(F,S);
          TT:=0;
          IY:=0;
          For I:=1 to length(S) do
          if copy(S,I,1)=',' then
            begin
              IY:=IY+1;
              LgP[IX,IY].N:=strtoint(copy(S,TT+1,I-TT-1));
              TT:=I;
            end;
          readln(F,S);
          TT:=0;
          IY:=0;
          For I:=1 to length(S) do
          if copy(S,I,1)=',' then
            begin
              IY:=IY+1;
              LgP[IX,IY].Ncu:=strtoint(copy(S,TT+1,I-TT-1));
              TT:=I;
            end;
          readln(F,S);
          TT:=0;
          IY:=0;
          For I:=1 to length(S) do
          if copy(S,I,1)=',' then
            begin
              IY:=IY+1;
              LgP[IX,IY].R:=strtoint(copy(S,TT+1,I-TT-1));
              TT:=I;
            end;
        end;
    creatLG;
    end;
  closefile(F);
end;

procedure WorkLG;
var
IX,IY : byte;
begin
  for IX:=1 to 5 do
  begin
  if (ACU[IX].S>0) and (ACU[IX].S<VintAP)
  then
    begin
      if (ACU[IX].S=VintAP div 2) then
        begin
          ALg(ACU[IX].X,ACU[IX].Y);
          WinLG;
        end;
      ACU[IX].S:=ACU[IX].S+1;
      ACU[IX].M.RollAngle:=ACU[IX].M.RollAngle+250*dt;
    end;
  if ACU[IX].S>(VintAP+1)
  then
    begin
      if (ACU[IX].S=round(VintAP*1.5)) then KroFF(ACU[IX].X,ACU[IX].Y);
      ACU[IX].S:=ACU[IX].S-1;
      ACU[IX].M.RollAngle:=ACU[IX].M.RollAngle-250*dt;
    end;

   if (BCU[IX].M.CurrentAnimation='bar1') and (BCU[IX].M.CurrentFrame=25) then
     begin
       BCU[IX].M.SwitchToAnimation('bar2');
       BCU[IX].M.AnimationMode:=aamLoop;
     end;
   if ACU[IX].S=(VintAP+1) then ACU[IX].S:=0;
  end;
  for IX:=1 to MPX do
  for IY:=1 to MPY do
  rollTr(IX,IY);
end;

procedure MRClick(X,Y : word);
var
  PLGO : TGLCustomSceneObject;
  OX, OY, IX, IY : byte;
begin
  logaut.GlLgUnVis.Visible:=true;
  GLInterface.Visible:=false;

  if Setup.FullWin then
  PLGO:= OForm.FullViewer.Buffer.GetPickedObject(X, Y) as TGLCustomSceneObject
  else
  if Viewer<>nil then PLGO:= Viewer.Buffer.GetPickedObject(X, Y) as TGLCustomSceneObject;

  if (PLGO<>nil) and (copy(PLGO.Name,0,3)='NVL') then
   begin
    OX:=round(PLGO.Position.X+(MPX div 2 +0.5));
    OY:=round(PLGO.Position.Y+(MPY div 2 +0.5));
    if LgU[OX,OY].T>0 then
      begin
        if (Lgp[OX,OY].Ncu>0) and (Lgp[OX,OY].Ncu<6)
        then
          begin
            Sound.PSoundY('str');
            if (ACU[Lgp[OX,OY].Ncu].S=0) or (ACU[Lgp[OX,OY].Ncu].S=(VintAP+1)) then
            ACU[Lgp[OX,OY].Ncu].S:=1
            else
            ACU[Lgp[OX,OY].Ncu].S:=VintAP+1+ACU[Lgp[OX,OY].Ncu].S;

            for IX:=1 to MPX do
            for IY:=1 to MPY do
            LgU[IX,IY].U:=true;

          end
        else
        if (Lgp[OX,OY].Ncu=0) then
          begin
            if (ACU[1].S=0) and (ACU[2].S=0) and (ACU[3].S=0)
            and (ACU[4].S=0) and (ACU[5].S=0) then
              begin
                Sound.PSoundY('rla');
                if ML then TrLeft(OX,OY);
                if MR then TrRight(OX,OY);
                Clicks:=Clicks+1;
                ghload.GLLabel[2].O.Caption:='Кликов совершено: '+Inttostr(Clicks);
              end;
          end;
        GetSA(Ox,Oy);
      end;
   end;
  GLInterface.Visible:=true;
  logaut.GlLgUnVis.Visible:=false;
end;




end.
