unit uMap;
interface
// Для любителей поковырять сырцы:
// Да, я опять написал процедурный хомнокод.
// Да, я взялся за старое, но все это потому,
// что:
// 1. такой код мне отладить быстрее, чем ООП огород и списки;
// 2. думаю, не стоит без необходимости плодить лишние сущности
// 3. создается ощущуение, что есть небольшие отличия в реализации ООП
// в разных языках (для справки, прототип написан на JavaScript с использованием
// ООП)
// вот так, будь у меня больше времени, возможно я бы переписал этот код
// в красивый, объектно ориентированный и логичный.

uses
  Classes, zglHeader, uMap4Way;

const
  c_Tile_dX  = 36;
 // c_Tile_d2X = 72;
  c_Tile_dY  = 18;
//  c_Tile_d2Y = 36;

 // c_Tile_pY  = 31;

type
  TDynObj = class;

  TTile = record
    pX, pY, pZ : Integer;
    tX, tY : Integer;
    Sprite : byte;

    DObj   : TDynObj;

    Visible : Boolean;  {?}
    MyObj  : Boolean;
    FH,
    Free   : Boolean;

  end;

  PTile = ^TTile;

  { TMap }

  TMap = class(TMap4Way)
    Tile0 : TTile;
    MouseMask : array of array of PTile;
    TileA : array of array of TTile;

    // Размеры карты
    MapX, MapY,
    MapW, MapH : Integer;
    hX, hY,
    camX, camY : Integer;

    Obj : TList;

    EnemyDynObj,
    Hero : TDynObj;

    Fight : Boolean;

    constructor Create(W_, H_ : Integer; WinW_, MapH_ : Integer);

    procedure Update;
    procedure Draw;
    function  MouseMove(const X_, Y_: Integer; Obj: byte; var outX, outY : Integer) : Boolean;
    function  FreeWay(X, Y : Integer; ImHero : Boolean) : Boolean;
    function  GetRandPos(var X, Y : Integer) : Boolean;
    function  GetFreeCount: Integer;
    function  SetWay(DynObj : TWayObj; FinishX, FinishY : Integer) : Boolean; override;
    private
    // Маска тайла (для определения тайла по координатам точки)
    c_Tile_Mask : array[0 .. 70 + 1, 0..35 + 1] of integer;

  end;



  { TDynObj }

  TDynObj = class(TWayObj)
    Live  : Integer;
    Heart : Integer;
    Sword : Integer;

    Map : TMap;
    ILiveObj : Boolean;

    procedure Update; virtual; abstract;
    procedure Draw; virtual; abstract;

  end;

var
  FreeObj : TDynObj;

implementation
uses
  uGlobal, uHero, uLiveObj;

 {
procedure PrepareFreeObj;
begin
  FreeObj := TDynObj.Create;
end;
}

{ Map }

constructor TMap.Create(W_, H_: Integer; WinW_, MapH_: Integer);

// Создает маску тайла (для определения тайла по координатам точки)
 procedure Create_MouseMask;
var
  iX, iY, zX : Integer;
begin
  for iX := 0 to 71 do
  for iY := 0 to 36 do
    c_Tile_Mask [iX, iY] := 0;

  for iX := 0 to 71 div 2 do
  begin
    for iY := 0 to 36 div 2 do
    if (iY < (36 - iX) / 2) then
      c_Tile_Mask[iX, iY] := 1;
    for iY := trunc(36 / 2) to 36 do
    if (iY >= (36 + 1 + iX) / 2) then
      c_Tile_Mask[iX, iY] := 1;
  end;

  for iX := 71 div 2 to 70 do
  begin
    zX := 71 - iX - 1;
    for iY := 0 to 36 div 2 do
    if (iY < (36 - zX) / 2) then
      c_Tile_Mask[iX, iY] := 1;
    for iY := trunc(36 / 2) to 36 do
    if (iY >= (36 + 1 + zX) / 2) then
      c_Tile_Mask[iX, iY] := 1;
  end;

end;

var
  iX, iY, iX2, iY2, tmX, tmY : Integer;
begin
  W := W_ - 1;
  H := H_ - 1;
  SetLength(TileA, W_, H_);

  camX := H*36;
  camY := 77;

  for iX := 0 to W do
  for iY := 0 to H do
  with TileA[iX, iY] do
  begin
    FH := false;
    if random > 0.8 then
      Sprite    := random(11)
    else
    if random > 0.5 then
      Sprite    := random(2)
    else
      Sprite    := 0;

    if Sprite = 10 then
      Sprite := 3;

    if (Sprite = 2) and (random > 0.6) then
      Sprite := 2;

    Visible   := true;
    Free      := Sprite <= 1;

    tX := iX;
    tY := iY;
    pX := (iX - iY) * 36 + camX;
    pY := (iX + iY) * 18 + camY;
  end;
  {
  with TileA[0, 0] do writeln(pX, ' ', pY + 2*pZ);
  with TileA[W, 0] do writeln(pX, ' ', pY + 2*pZ);
  with TileA[0, H] do writeln(pX, ' ', pY + 2*pZ);
  with TileA[W, H] do writeln(pX, ' ', pY + 2*pZ); }

  // Создаем маску тайла (для определения тайла по координатам точки)
  Create_MouseMask;

  Tile0.Visible := false;
  Tile0.tX := - 1;
  Tile0.tX := - 1;

  MapX := TileA[0, H].pX;
  MapY := TileA[0, 0].pY + 2*TileA[W, H].pZ - 77;
  MapW := TileA[W, 0].pX - MapX + 70;
  MapH := TileA[W, H].pY - MapY + 2*TileA[W, H].pZ - 40;
  //writeln(MapX, ' ', MapY, ' ', MapW, ' ', MapH, ' ');

  camX := (WinW - MapW) div 2;
  camY := (WinH - MapH) div 2;

  SetLength(MouseMask, MapW + 1, MapH + 1);

  for iX := 0 to MapW do
  for iY := 0 to MapH do
    MouseMask[iX, iY] := @Tile0;

  for iX := 0 to W do
  for iY := 0 to H do
  for iX2 := 0 to 70 do
  for iY2 := 0 to 36 do
  if c_Tile_Mask[iX2, iY2] = 0 then
  with TileA[iX, iY] do
  begin
    tmX := trunc(pX) + iX2 ;
    tmY := trunc(pY) + 2*pZ + iY2 - 80;
    if  (tmX > 0) and (tmX < MapW)
    and (tmY > 0) and (tmY < MapH) then
      MouseMask[tmX, tmY] := @TileA[iX, iY];
  end;

  Obj := TList.Create;

  SetLength(FreeTile, W + 1, H + 1);
end;

procedure TMap.Draw;
var
  iX, iY : Integer;

begin
  for iY := 0 to H do
  for iX := 0 to W do
  with TileA[iX, iY] do
  if Visible then
  begin
    //Ssprite2d_Draw(texTiles, pX, pY + 2*pZ, 128, 256, 0);
    {if Free then
    sTiles.DrawCB(pX + camX, pY + 2*pZ + camY + 5, Sprite)
    else  }
    sTiles.DrawCB(pX + camX, pY + 2*pZ + camY, Sprite);
    { if Obj > 0 then
    begin
      Asprite2d_Draw(cube, pX - 64 + 35, pY, 128, 128, 0, Obj);
      Asprite2d_Draw(cube, pX - 64 + 35, round(pY - 32 - sin(ggY)*10), 128, 128, 0, Obj);
    end; }
    if DObj <> nil then
      DObj.Draw;
  end;

  //pr2d_Rect(camX + MapX, camY + MapY, MapW, MapH);
end;

function TMap.FreeWay(X, Y: Integer; ImHero : Boolean): Boolean;
begin
  if (X >= 0) and (X <= W) and (Y >= 0) and (Y <= H) then
    result := TileA[X][Y].Free and (ImHero or not TileA[X][Y].FH)
  else
    result := false;
end;

function TMap.MouseMove(const X_, Y_: Integer; Obj: byte; var outX, outY : Integer) : Boolean;
var
  X, Y, mX_, mY_ : Integer;
begin
  for X := 0 to W do
  for Y := 0 to H do
    TileA[X][Y].pZ := 0;

  mX_ := X_ - camX + mapX;
  mY_ := Y_ - camY + mapY;

  if (mX_ > 0) and (mX_ < MapW) and (mY_ > 0) and (mY_ < MapH) then
  begin
    X := MouseMask[mX_, mY_].tX;
    Y := MouseMask[mX_, mY_].tY;
    outX := X;
    outY := Y;
    Result := false;

    if (X >= 0) and (X <= W) and (Y >= 0) and (Y <= H) and (TileA[X][Y].Free) then
      TileA[X][Y].pZ := 1;

    if mouse_Click(M_BLEFT) then
    begin
      if (X >= 0) and (X <= W) and (Y >= 0) and (Y <= H) then
        SetWay(Hero, X, Y);
    end;
  end;

end;

procedure TMap.Update;
var
  I : Integer;
begin
  Fight := false;
  for I := Obj.Count - 1 downto 0 do
    TLiveObj(Obj[I]).Update;
end;

function TMap.GetRandPos(var X, Y: Integer): Boolean;
var
  iX, iY : Integer;
  FreeCount : Integer;
begin
  FreeCount := 0;
  for iX := 0 to W do
  for iY := 0 to H do
  if TileA[iX][iY].Free then
    FreeCount := FreeCount + 1;

  if FreeCount > 10 then
  begin
    repeat
      X := random(W + 1);
      Y := random(H + 1);
    until TileA[X][Y].Free;
    Result := true;
  end
  else
    Result := false;
end;

function TMap.GetFreeCount: Integer;
var
  iX, iY : Integer;
  FreeCount : Integer;
begin
  FreeCount := 0;
  for iX := 0 to W do
  for iY := 0 to H do
  if TileA[iX][iY].Free then
    FreeCount := FreeCount + 1;
  Result := FreeCount;
end;

function TMap.SetWay(DynObj: TWayObj; FinishX, FinishY: Integer): Boolean;
var
  X, Y : Integer;
begin
  for X := 0 to W do
  for Y := 0 to H do
    FreeTile[X, Y] := TileA[X][Y].Free ;//(TileA[X][Y].Sprite < 2)
  Result:=inherited SetWay(DynObj, FinishX, FinishY);
end;



end.
