//////////////////////////////////////////////////////////////////////////////////
// Project Name:   [ CDX Class Library - CDX.lib ]
// Source File:    [ CDXMap Implementation ]
// Author:				 [ Danny Farley - danny@jags.co.uk ]
// Contributions:  [ Keith Fuller ]
// Revision:       [ 1.4 ]
/////////////////////////////////////////////////////////////////////////////////

unit CDXMapX;

interface

uses
  Windows, DirectDraw, Classes, CDXTileX, CDXScreenX;

type
//////////////////////////////////////////////////////////////////////////////////
// CDXMap Class
//////////////////////////////////////////////////////////////////////////////////
  CDXMap = class
	constructor CDXMap(pTiles : CDXTile;pScreen : CDXScreen);
        destructor Destroy; override;

	procedure CreateMap(Width,Height,Fill : Integer);
	function Load(const szFilename : PChar) : BOOL;
	function Save(szFilename : PChar) : BOOL;
        procedure Clear;
	procedure Fill(TileNum : Integer);
	procedure BltTile(lpDDS : CDXSurface;xdest,ydest,w,h,xoff,yoff : Integer);
	procedure BltTileTrans(lpDDS : CDXSurface;xdest,ydest,w,h,xoff,yoff : Integer);
	procedure Draw(lpDDS : CDXSurface);
	procedure DrawTrans(lpDDS : CDXSurface);
        procedure DrawClipped(lpDDS : CDXSurface;ClipDestRect : TRect);
	procedure MoveTo(PosX,PosY : Integer);
	procedure ScrollUp(Offset : Integer);
	procedure ScrollDown(Offset : Integer);
	procedure ScrollLeft(Offset : Integer);
	procedure ScrollRight(Offset : Integer);
	procedure WrapScrollUp(Offset : Integer);
	procedure WrapScrollDown(Offset : Integer);
	procedure WrapScrollLeft(Offset : Integer);
	procedure WrapScrollRight(Offset : Integer);
	procedure ScreenTileSize(Width,Height : Integer);
	function GetTile(MapX,MapY : Integer) : Integer;
	procedure SetTile(MapX,MapY,Tile : Integer);
	procedure LoadTiles(pTiles : CDXTile);

  public
	m_PosX, m_PosY : Integer;
	m_Width, m_Height : Integer;
	m_TileWidth, m_TileHeight : Integer;
	m_PixelWidth, m_PixelHeight : Integer;
	SCREEN_TW, SCREEN_TH : Integer;
	SCREEN_W, SCREEN_H : Integer;
	SIZE : Integer;
	DATA : PInteger;
        Screen : CDXScreen;
	Tiles : CDXTile;
  end;

implementation

//////////////////////////////////////////////////////////////////////////////////
// CDXMap Constructor
//////////////////////////////////////////////////////////////////////////////////
constructor CDXMap.CDXMap(pTiles : CDXTile;pScreen : CDXScreen);
begin
  DATA := nil;
  Tiles := pTiles;

  m_TileWidth := Tiles.m_BlockWidth;
  m_TileHeight := Tiles.m_BlockHeight;

  m_PosX := 0;
  m_PosY := 0;

  SCREEN_W := pScreen.m_dwPixelWidth;
  SCREEN_H := pScreen.m_dwPixelHeight;

  SCREEN_TW := trunc(SCREEN_W / m_TileWidth);
  SCREEN_TH := trunc(SCREEN_H / m_TileHeight);
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap Destructor
//////////////////////////////////////////////////////////////////////////////////
destructor CDXMap.Destroy;
begin
  FreeMem(DATA,SIZE * SizeOf(Integer));
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap Create
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.CreateMap(Width,Height,Fill : Integer);
var
  i : Integer;
  p : PInteger;
begin
  m_Width := Width;
  m_Height := Height;

  m_PixelWidth := m_Width  *  m_TileWidth;
  m_PixelHeight := m_Height  *  m_TileHeight;

  SIZE := m_Width  *  m_Height;
  GetMem(DATA,SIZE * SizeOf(Integer));
  for i := 0 to SIZE - 1 do begin
      p := Pointer(Integer(Data) + i * SizeOf(Integer));
      p^ := Fill;
  end;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap LoadMap
//////////////////////////////////////////////////////////////////////////////////
function CDXMap.Load(const szFilename : PChar) : BOOL;
var
  fb : file;
begin
  Assign(fb,szFilename);

  Reset(fb);
  BlockRead(fb,m_Width,SizeOf(Integer));
  BlockRead(fb,m_Height,SizeOf(Integer));

  SIZE := m_Width * m_Height;
  GetMem(DATA,SIZE * SizeOf(Integer));

  BlockRead(fb,DATA^,SIZE * SizeOf(Integer));

  Close(fb);

  m_PixelWidth := m_Width * m_TileWidth;
  m_PixelHeight := m_Height * m_TileHeight;
  Load := True;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap SaveMap
//////////////////////////////////////////////////////////////////////////////////
function CDXMap.Save(szFilename : PChar) : BOOL;
var
  fb : file;
begin
{$I-}
  Assign(fb,szFilename);
  Rewrite(fb);
  BlockWrite(fb,m_Width,SizeOf(Integer));
  BlockWrite(fb,m_Height,SizeOf(Integer));
  SIZE := m_Width * m_Height;
  BlockWrite(fb,DATA^,SIZE * SizeOf(Integer));
  Close(fb);
  Save := true;
{$I+}
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap Clear
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.Clear;
var
  i : Integer;
  p : PInteger;
begin
  for i := 0 to SIZE - 1 do begin
      p := Pointer(Integer(Data) + i * SizeOf(Integer));
      p^ := 0;
  end;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap Fill
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.Fill(TileNum : Integer);
var
  i : Integer;
  p : PInteger;
begin
  for i := 0 to SIZE - 1 do begin
  p := Pointer(Integer(DATA) + i * SizeOf(Integer));
  p^ := TileNum;
  end;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap MoveTo
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.MoveTo(PosX,PosY : Integer);
begin
  m_PosX := PosX;
  m_PosY := PosY;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap ScrollUp
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.ScrollUp(Offset : Integer);
begin
  m_PosY := m_PosY - Offset;
  if m_PosY<0 then m_PosY := 0;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap ScrollDown
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.ScrollDown(Offset : Integer);
begin
  m_PosY := m_PosY + Offset;
  if (m_PosY>(m_Height - SCREEN_TH) * m_TileHeight) then
  m_PosY := (m_Height - SCREEN_TH) * m_TileHeight;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap ScrollLeft
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.ScrollLeft(Offset : Integer);
begin
  m_PosX := m_PosX + Offset;
  if (m_PosX<0) then m_PosX :=  0;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap ScrollRight
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.ScrollRight(Offset : Integer);
begin
  m_PosX := m_PosX + Offset;
  if (m_PosX>(m_Width - SCREEN_TW) * m_TileWidth) then
  m_PosX := (m_Width - SCREEN_TW) * m_TileWidth;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap WrapScrollUp
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.WrapScrollUp(Offset : Integer);
begin
  if ((m_PosY - Offset)>=0)
     then m_PosY := m_PosY - Offset
     else m_PosY := (m_Height * m_TileHeight) - Offset + m_PosY;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap WrapScrollDown
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.WrapScrollDown(Offset : Integer);
begin
  if ((m_PosY + Offset)<(m_Height  *  m_TileHeight))
     then m_PosY := m_PosY + Offset
     else m_PosY := Offset - (m_Height * m_TileHeight) + m_PosY;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap WrapScrollLeft
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.WrapScrollLeft(Offset : Integer);
begin
  if ((m_PosX - Offset)>=0)
     then m_PosX := m_PosX - Offset
     else m_PosX := (m_Width * m_TileWidth) - Offset + m_PosX;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap WrapScrollRight
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.WrapScrollRight(Offset : Integer);
begin
  if ((m_PosX  +  Offset)<(m_Width  *  m_TileWidth))
     then m_PosX := m_PosX + Offset
     else m_PosX := Offset - (m_Width * m_TileWidth) + m_PosX;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap ScreenTileSize
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.ScreenTileSize(Width,Height : Integer);
begin
  SCREEN_TW := Width;
  SCREEN_TH := Height;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap GetTile
//////////////////////////////////////////////////////////////////////////////////
function CDXMap.GetTile(MapX,MapY : Integer) : Integer;
var
  p : PInteger;
begin
  p := Pointer(Integer(DATA) + (MapX + (MapY * m_Width)) * SizeOf(Integer));
  GetTile := p^;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap SetTile
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.SetTile(MapX,MapY,Tile : Integer);
var
  p : PInteger;
begin
  p := Pointer(Integer(DATA) + (MapX + (MapY * m_Width)) * SizeOf(Integer));
  p^ := Tile;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap LoadTiles
//////////////////////////////////////////////////////////////////////////////////
{$WARNINGS OFF}
{$HINTS OFF}
procedure CDXMap.LoadTiles(pTiles : CDXTile);
begin
  pTiles := Tiles;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap BltTile
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.BltTile(lpDDS : CDXSurface;xdest,ydest,w,h,xoff,yoff : Integer);
var
  TILE_SW : Integer;
  p : PInteger;
  src : TRect;
  tile_num : Integer;
  x1,y1 : Integer;
  mapx,mapy : Integer;
begin
  mapx := Trunc(((m_PosX  +  xdest) mod m_PixelWidth)/m_TileWidth);
  mapy := Trunc(((m_PosY  +  ydest) mod m_PixelHeight)/m_TileHeight);

  p := PInteger(Integer(DATA) + (mapx + (mapy * m_Width)) * SizeOf(Integer));
  tile_num := p^;

  if tile_num<> - 1 then begin
  TILE_SW := Trunc(Tiles.m_PixelWidth/m_TileWidth);
  x1 := tile_num mod TILE_SW;
  x1 := (x1 * m_TileWidth) + xoff;

  y1 := Trunc(tile_num/TILE_SW);
  y1 := (y1 * m_TileHeight) + yoff;

  src.top := y1;
  src.left := x1;
  src.bottom := y1 + h;
  src.right := x1 + w;

  lpDDS.m_lpDDS.BltFast(xdest,ydest,Tiles.m_lpDDS,@src,DDBLTFAST_WAIT);
  end;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap BltTile
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.BltTileTrans(lpDDS : CDXSurface;xdest,ydest,w,h,xoff,yoff : Integer);
var
  TILE_SW : Integer;
  src : TRect;
  tile_num : Integer;
  x1,y1 : Integer;
  mapx,mapy : Integer;
begin
  mapx := ((m_PosX + xdest) mod m_PixelWidth) div m_TileWidth;
  mapy := ((m_PosY + ydest) mod m_PixelHeight) div m_TileHeight;

  tile_num := PInteger(Integer(DATA) + (mapx + (mapy * m_Width)) * SizeOf(Integer))^;

  if tile_num <> - 1 then begin

     TILE_SW := Trunc(Tiles.m_PixelWidth/m_TileWidth);

     x1 := tile_num mod TILE_SW;
     x1 := (x1 * m_TileWidth) + xoff;

     y1 := Trunc(tile_num/TILE_SW);
     y1 := (y1  *  m_TileHeight) + yoff;

     src.top := y1;
     src.left := x1;
     src.bottom := y1 + h;
     src.right := x1 + w;

     lpDDS.m_lpDDS.BltFast(xdest,ydest,Tiles.m_lpDDS,@src,DDBLTFAST_WAIT + DDBLTFAST_SRCCOLORKEY);
  end;
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap Draw
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.Draw(lpDDS : CDXSurface);
var
  i, j : Integer;
  xoffset, yoffset : Integer;
  xcoord,ycoord : Integer;
begin
  xcoord := 0;
  ycoord := 0;
  xoffset := m_PosX mod m_TileWidth;
  yoffset := m_PosY mod m_TileHeight;

  // FIRST ROW
  BltTile(lpDDS,0,0,m_TileWidth - xoffset,m_TileWidth - yoffset,xoffset,yoffset);

  for i := 0 to (SCREEN_TW - 2) do begin
      xcoord := xcoord + m_TileWidth;
      BltTile(lpDDS,xcoord - xoffset,0,m_TileWidth,m_TileHeight - yoffset,0,yoffset);
  end;

  xcoord := xcoord + m_TileWidth;
  BltTile(lpDDS,xcoord - xoffset,0,xoffset,m_TileHeight - yoffset,0,yoffset);

  // NEXT X NUMBER OF ROWS
  for j := 0 to (SCREEN_TH - 2) do begin
      xcoord := m_TileWidth;
      ycoord := ycoord + m_TileHeight;
      BltTile(lpDDS,0,ycoord - yoffset,m_TileWidth - xoffset,m_TileHeight,xoffset,0);

      for i := 0 to (SCREEN_TW - 2) do begin
  	  BltTile(lpDDS,xcoord - xoffset,ycoord - yoffset,m_TileWidth,m_TileHeight,0,0);
  	  xcoord := xcoord + m_TileWidth;
      end;

      BltTile(lpDDS,xcoord - xoffset,ycoord - yoffset,xoffset,m_TileHeight,0,0);
  end;

  // LAST ROW
  xcoord := 0;
  ycoord := ycoord + m_TileHeight;
  BltTile(lpDDS,0,ycoord - yoffset,m_TileWidth - xoffset,yoffset,xoffset,0);

  for i := 0 to (SCREEN_TW - 2) do begin
      xcoord := xcoord + m_TileWidth;
      BltTile(lpDDS,xcoord - xoffset,ycoord - yoffset,m_TileWidth,yoffset,0,0);
  end;

  xcoord := xcoord + m_TileWidth;
  BltTile(lpDDS,xcoord - xoffset,ycoord - yoffset,xoffset,yoffset,0,0);
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap DrawTrans
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.DrawTrans(lpDDS : CDXSurface);
var
  i,j : Integer;
  xoffset, yoffset : Integer;
  xcoord,ycoord : Integer;
begin
  xcoord := 0;
  ycoord := 0;
  xoffset := m_PosX mod m_TileWidth;
  yoffset := m_PosY mod m_TileHeight;

  // FIRST ROW
  BltTileTrans(lpDDS,0,0,m_TileWidth - xoffset,m_TileWidth - yoffset,xoffset,yoffset);

  for i := 0 to (SCREEN_TW - 2) do begin
  	xcoord := xcoord + m_TileWidth;
  	BltTileTrans(lpDDS,xcoord - xoffset,0,m_TileWidth,m_TileHeight - yoffset,0,yoffset);
  end;

  xcoord := xcoord + m_TileWidth;
  BltTileTrans(lpDDS,xcoord - xoffset,0,xoffset,m_TileHeight - yoffset,0,yoffset);

  // NEXT X NUMBER OF ROWS
  for j := 0 to (SCREEN_TH - 2) do begin
      xcoord := m_TileWidth;
      ycoord := ycoord + m_TileHeight;
      BltTileTrans(lpDDS,0,ycoord - yoffset,m_TileWidth - xoffset,m_TileHeight,xoffset,0);

      for i := 0 to (SCREEN_TW - 2) do begin
      	BltTileTrans(lpDDS,xcoord - xoffset,ycoord - yoffset,m_TileWidth,m_TileHeight,0,0);
      	xcoord := xcoord + m_TileWidth;
      end;

      BltTileTrans(lpDDS,xcoord - xoffset,ycoord - yoffset,xoffset,m_TileHeight,0,0);
  end;

  // LAST ROW
  xcoord := 0;
  ycoord := ycoord + m_TileHeight;
  BltTileTrans(lpDDS,0,ycoord - yoffset,m_TileWidth - xoffset,yoffset,xoffset,0);

  for i := 0 to (SCREEN_TW - 2) do begin
      xcoord := xcoord + m_TileWidth;
      BltTileTrans(lpDDS,xcoord - xoffset,ycoord - yoffset,m_TileWidth,yoffset,0,0);
  end;

  xcoord := xcoord + m_TileWidth;
  BltTileTrans(lpDDS,xcoord - xoffset,ycoord - yoffset,xoffset,yoffset,0,0);
end;

//////////////////////////////////////////////////////////////////////////////////
// CDXMap DrawClipped
//////////////////////////////////////////////////////////////////////////////////
procedure CDXMap.DrawClipped(lpDDS : CDXSurface;ClipDestRect : TRect);
var
  i,j : Integer;
  xoffset, yoffset : Integer;
  xcoord,ycoord : Integer;
  minwidth,minheight : Integer;
begin
  xoffset := (m_PosX + ClipDestRect.left) mod m_TileWidth;
  yoffset := (m_PosY + ClipDestRect.top) mod m_TileWidth;
  xcoord := 0;
  ycoord := 0;
  if (m_TileWidth - xoffset)<(ClipDestRect.right - ClipDestRect.left) then
  minwidth := m_TileWidth - xoffset
  else
  minwidth := ClipDestRect.right - ClipDestRect.left;
  if (m_TileHeight - yoffset)<(ClipDestRect.bottom - ClipDestRect.top) then
  minheight := m_TileHeight - yoffset
  else
  minheight := ClipDestRect.bottom - ClipDestRect.top;

  // FIRST ROW
  BltTile(lpDDS, ClipDestRect.left, ClipDestRect.top, minwidth, minheight, xoffset, yoffset);
  xcoord := ClipDestRect.left + minwidth;

  while ((xcoord  +  m_TileWidth) < ClipDestRect.right) do begin
 	BltTile(lpDDS, xcoord, ClipDestRect.top, m_TileWidth, minheight, 0, yoffset);
  	xcoord := xcoord + m_TileWidth;
  end;
  BltTile(lpDDS, xcoord, ClipDestRect.top, ClipDestRect.right - xcoord,	minheight, 0, yoffset);
  // NEXT X NUMBER OF ROWS
  ycoord := ClipDestRect.top + minheight;

  while((ycoord  +  m_TileHeight) < ClipDestRect.bottom) do begin
  	BltTile(lpDDS, ClipDestRect.left, ycoord, minwidth, m_TileHeight,	xoffset, 0);
  	xcoord := ClipDestRect.left + minwidth;

  	while((xcoord  +  m_TileWidth) < ClipDestRect.right) do begin
  		BltTile(lpDDS, xcoord, ycoord, m_TileWidth, m_TileHeight, 0, 0);
  		xcoord := xcoord + m_TileWidth;
  	end;

  	BltTile(lpDDS, xcoord, ycoord, ClipDestRect.right - xcoord,	m_TileHeight, 0, 0);
  	ycoord := ycoord + m_TileHeight;
  end;

  // LAST ROW
  BltTile(lpDDS, ClipDestRect.left, ycoord, minwidth, ClipDestRect.bottom - ycoord, xoffset, 0);
  xcoord := ClipDestRect.left + minwidth;

  while((xcoord  +  m_TileWidth) < ClipDestRect.right) do begin
  	BltTile(lpDDS, xcoord, ycoord, m_TileWidth, ClipDestRect.bottom  - ycoord, 0, 0);
  	xcoord := xcoord + m_TileWidth;
  end;

  BltTile(lpDDS, xcoord, ycoord, ClipDestRect.right - xcoord,	ClipDestRect.bottom - ycoord, 0, 0);
end;

{$WARNINGS ON}
{$HINTS ON}

end.
