unit u_map;

interface

uses SysUtils, Windows, Graphics, zglHeader, zglGUI, OpenGl, u_const, PAthFind, u_music;

type
     TMapBit = record
       btType: Byte;
       btWallId: Byte;
     end;
     TMap = object
       public
         MapW, MapH, EatCount, EatCountMax, gid:Integer;
         PlayerPos:zglTPoint2D;
         MapData: array of array of TMapBit;
         procedure LoadFromFile(s:string);
         procedure Render(X,Y, Angle:single; Level:Integer=0);
         function CkeckPos(X,Y:single; Angle:Integer;DetectSleep:boolean):boolean;
         function InCenter(X,Y:single):boolean;
         procedure aNormalize(var a:integer);
         function InRec(B:integer; X,Y:single):boolean;
         procedure Say(Pos:zglTPoint2D; s:string);
         procedure Reload;
       private
         MapPath:string;
         function WallId(X,Y:integer):Byte;
     end;

implementation

uses u_game;

procedure TMap.aNormalize(var a:integer);
begin
  while a<0 do a:=a+360; while a>=360 do a:=a-360;
end;

procedure TMap.Reload;
var i:integer;
begin
  Player.Init(Round(PlayerPos.X), Round(PlayerPos.Y));
  for i:=1 to gid do
    Ghosts[i].Init(i, Round(Ghosts[i].Home.X), Round(Ghosts[i].Home.Y));
end;

procedure TMap.Say;
var X,Y:single;
begin
  X:=Pos.X*item_size;
  Y:=Pos.Y*item_size;

  pr2d_Rect(X-length(S)*symbolWidth/2-4, Y-64, length(S)*symbolWidth+8, symbolHeight+4, $333333, 255, PR2D_FILL);
  pr2d_Rect(X-length(S)*symbolWidth/2-4, Y-64, length(S)*symbolWidth+8, symbolHeight+4, $FFFFFF);

  guiDrawText(S, X, Y-66, align_center);
end;

function TMap.InRec(B:integer; X,Y:single):boolean;
var i,j:integer;
    off:single;
begin
  off:=0.2;
  x:=x-Round(x); y:=y-round(y);
  result:=false;
  if b in [1,8] then b:=9;
  if b in [2,4] then b:=6;
  if b and 1>0 then
    if (x>=-off) and (x<=off) and (y>=-0.5) and (y<=0) then result:=true;
  if b and 2>0 then
    if (x>=-0.5) and (x<=0) and (y>=-off) and (y<=off) then result:=true;
  if b and 4>0 then
    if (x>=0) and (x<=0.5) and (y>-off) and (y<=off) then result:=true;
  if b and 8>0 then
    if (x>=-off) and (x<=off) and (y>=0) and (y<=0.5) then result:=true;
end;

function TMap.CkeckPos(X,Y:single; Angle:Integer;DetectSleep:boolean):boolean;
var i,a1,a2:integer;
begin
  if (X<0) or (Y<0) or (X>MapW-1) or (Y>MapH-1) then begin
    result:=False;
    exit;
  end;
  result:=not ((MapData[Round(X),Round(Y)].btType in [bt_wall, bt_badwall]) or (DetectSleep and (MapData[Round(X),Round(Y)].btType=bt_sleepwall)));
  
  for i:=1 to portals_count do
    if (Portals[i].State=portal_opened) and (Portals[Portals[i].ExitId].State=portal_opened) then
    if (Round(Portals[i].Position.X)=Round(X)) and (round(Portals[i].Position.Y)=Round(Y)) and
      (true) then begin
      a1:=Portals[i].lookAngle;
      aNormalize(a1);
      a2:=Round(Angle);
      aNormalize(a2);
      if ((Abs(a1-a2)<45) or (Abs(a1-a2)>325)) then
        result:=true;
    end;
end;

function TMap.InCenter(X,Y:single):boolean;
begin
  result:=(Abs(X-Round(X))<player_speed*2) and (Abs(Y-Round(Y))<player_speed*2);
end;

function TMap.WallId(X,Y:integer):Byte;
var i:Byte;
begin
  i:=0;
  if X>0 then
    if MapData[X-1,Y].btType in [bt_wall, bt_badwall, bt_sleepwall] then
      Inc(i,2);
  if Y>0 then
    if MapData[X,Y-1].btType in [bt_wall, bt_badwall, bt_sleepwall] then
      Inc(i,1);
  if X<MapW-2 then
    if MapData[X+1,Y].btType in [bt_wall, bt_badwall, bt_sleepwall] then
      Inc(i,4);
  if Y<MapH-2 then
    if MapData[X,Y+1].btType in [bt_wall, bt_badwall, bt_sleepwall] then
      Inc(i,8);
  if i=0 then i:=16;
  result:=i;
end;

procedure TMap.Render(X,Y, Angle:single; Level:Integer=0);
var  i,m,a2,a1:integer;
     cm:zglTCamera2D;
     _x:zglTPoint2D;
     d,s:single;
begin
  cm.X:=Round(X);
  cm.Y:=Round(Y);
  cm.Zoom.X:=1;
  cm.Zoom.Y:=1;
  cm.Angle:=Angle;


  Cam2D_Set(@cm);


  
  pr2d_Rect(item_size div 2,item_size div 2,(mapW-2)*item_size, (maph-2)*item_size, $111111, 255, PR2D_FILL);
  zgl.Draw;

  for m:=0 to MapH-1 do
    for i:=0 to MapW-1 do begin
      case MapData[i,m].btType of
        bt_eat:begin
          asprite2d_Draw(txEat, (i-0.5)*item_size, (m-0.5)*item_size, item_size, item_size, 0, 1);
        end;
        bt_enetgizer:begin
          asprite2d_Draw(txEat, (i-0.5)*item_size, (m-0.5)*item_size, item_size, item_size, (j mod 720)/2, 2);
        end;
      end;
    end;

  Player.Render;

   for i:=1 to ghosts_count do
     if Ghosts[i].Enabled then Ghosts[i].Draw;

  for m:=0 to MapH-1 do
    for i:=0 to MapW-1 do begin
      case MapData[i,m].btType of
        bt_wall:begin
          asprite2d_Draw(txWalls, (i-0.5)*item_size, (m-0.5)*item_size, item_size, item_size,
            0, MapData[i,m].btWallId);
        end;
        bt_sleepwall:begin
          asprite2d_Draw(txSleepWalls, (i-0.5)*item_size, (m-0.5)*item_size, item_size, item_size,
            0, MapData[i,m].btWallId);
        end;
        bt_badwall:begin
          asprite2d_Draw(txbadWalls, (i-0.5)*item_size, (m-0.5)*item_size, item_size, item_size,
            0, MapData[i,m].btWallId);
        end;
      end;
    end;

  for i:=1 to portals_count do begin
    case Portals[i].State of
      portal_opened:begin

        asprite2d_Draw(txPortals,
          (portals[i].Position.X-0.5+m_cos(portals[i].lookAngle)*0.5)*item_size,
          (portals[i].Position.Y-1+m_sin(portals[i].lookAngle)*0.5)*item_size,
            item_size, item_size*2, portals[i].lookAngle, i);
        if Portals[i].DrawTwo>0 then m:=i+portals_count else m:=i;
          asprite2d_Draw(txPortals,
          (portals[i].Position.X-0.5+m_cos(portals[i].lookAngle)*0.5)*item_size,
          (portals[i].Position.Y-1+m_sin(portals[i].lookAngle)*0.5)*item_size,
            item_size, item_size*2, portals[i].lookAngle, i+portals_count, Portals[i].DrawTwo);
      end;
    end;
  end;


  if Player.SayTime>0 then
    Say(Player.Pos,Player.Say);
  if GameState=gs_choose then a1:=2 else a1:=portals_count;
  if Level<1 then
  for i:=1 to a1 do begin
    case Portals[i].State of
      portal_opened:begin
         a1:=Portals[i].lookAngle;
        while a1<0 do a1:=a1+360; while a1>360 do a1:=a1-360;
        a2:=Round(m_Angle(Player.Pos.X, Player.Pos.Y, Portals[i].Position.X, Portals[i].Position.Y));
        while a2<0 do a2:=a2+360; while a2>360 do a2:=a2-360;
        d:=m_Distance(Player.Pos.X, Player.Pos.Y, portals[i].Position.X, portals[i].Position.Y);
        if (Portals[Portals[i].ExitId].State=portal_opened) and
           (d<8) then begin
            m:=Portals[i].ExitId;
          if (((abs(a2-a1)<20) or (abs(a2-a1)>340)) or (d<0.5)) then begin

              rtarget_Set( RTrgt );
                Map.Render(Portals[m].Position.X*item_size-64,
                  Portals[m].Position.Y*item_size-64,
                  0, Level+1);

                fx_SetColorMask(FALSE, FALSE, FALSE, TRUE );
                fx_SetBlendMode(FX_BLEND_MULT);
                ssprite2d_Draw(txZeroMask, 0,0,128,128, portals[m].lookAngle);
                fx_SetBlendMode(FX_BLEND_NORMAL);
                fx_SetColorMask(TRUE, TRUE, TRUE, TRUE );

                ssprite2d_Draw(txPortalEnd, 0,0,128,128, portals[m].lookAngle);


              rtarget_Set( nil );
            Cam2D_Set(@cm);
            ssprite2d_Draw(RTrgt.Surface,
             Portals[i].Position.X*item_size - 64,
             Portals[i].Position.Y*item_size - 64, 128, 128, Angle+180+portals[i].lookAngle-portals[m].lookAngle-Player.AngleOffset);
          end;
          if d<=3 then begin
            if abs(a2-a1)<60 then s:= 1-abs(a2-a1)/60 else
              if abs(a2-a1)>300  then s:= 1-(360-abs(a2-a1))/60 else s:=0;
            Portals[m].DrawTwo:=Round(255*s*(1-d/3))
          end else Portals[m].DrawTwo:=0;
        end else Portals[Portals[i].ExitId].DrawTwo:=0;
      end;
    end;
  end;
  cam2d_Apply( nil );
end;                       

procedure TMap.LoadFromFile(s:string);
var bt:TBitmap;
    i,j, a:integer;
    btType, pid:integer;
begin
  if not FileExists(s) then exit;
  MapPath:=s;
  for i:=1 to ghosts_count do begin
    Ghosts[i].Enabled:=False;
  end;
  gid:=0;

  bt:=TBitmap.Create;
  bt.LoadFromFile(s);
  SetLength(MapData, bt.Width, bt.Height);
  MapW:=bt.Width;
  MapH:=bt.Height;

  InitMap(MapW, MapH);
  
  EatCount:=0; EatCountMax:=0;
  WizardId:=0;
  pid:=2;
  for j:=1 to portals_count do
    Portals[j].State:=portal_none;
  for j:=0 to MapH-1 do
    for i:=0 to MapW-1 do begin
      case bt.Canvas.Pixels[i,j] of
        $FFFFFF:btType:=bt_none;
        $404040:btType:=bt_badwall;
        $A0A0A0:begin
          btType:=bt_eat;
          inc(EatCount);
        end;
        $6E00FF:begin
          btType:=bt_enetgizer;
        end;
        $7F0021:begin
          btType:=bt_wizard;
        end;
        $00337F:begin
          btType:=bt_sleepwall;
        end;
        $FF9400:begin
          btType:=bt_none;
        end;
        $0000FF:begin
          btType:=bt_none;
          Player.Init(i,j);

          PlayerPos.X:=i;
          PlayerPos.Y:=j;
        end;
        $FF2600:begin
          btType:=bt_none;
          inc(gid);
          Ghosts[gid].Init(gid,i,j);
          Ghosts[gid].OutTime:=(gid-1)*out_time;
        end;
        else btType:=bt_wall;
      end;
      MapData[i,j].btType:=btType;
    end;
  EatCountMax:=EatCount;
  for j:=0 to MapH-1 do
    for i:=0 to MapW-1 do begin
      MapData[i,j].btWallId:=WallId(i,j);
      case bt.Canvas.Pixels[i,j] of
        $FF9400:begin
          inc(pid);
          Portals[pid].State:=portal_opened;
          if not (MapData[i,j+1].btType in [bt_wall, bt_badwall]) then
            a:=90
          else if not (MapData[i,j-1].btType in [bt_wall, bt_badwall]) then
            a:=270
          else if not (MapData[i-1,j].btType  in [bt_wall, bt_badwall]) then
            a:=180
          else a:=0;
          Portals[pid].lookAngle:=a;
          Portals[pid].Position.X:=i-m_cos(a);
          Portals[pid].Position.Y:=j-m_sin(a);
          Portals[pid].DrawTwo:=0;
        end;
      end;
    end;
  bt.Free;
end;

end.
