{
  Autor: Michael Springwald

  Erstellt: Montag, 19.November.2007
  Updates: Mittwoch, 21.November.2007, Donnerstag, 22.November.2007
           Freitag, 23.November.2007, Samstag, 24.November.2007
           Sontag, 25.November.2007, Mittwoch, 28.November.2007
           Donnerstag, 29.Novmeber.2007

}

unit uPluto2dEngine;

{$mode objfpc}{$H+}

interface

// ----------------------------------------------
uses
  opbitmap,Classes, SysUtils, Graphics, LCLIntf, LCLType, uOpExt, uPlutoImageList,
  contnrs, uplutogamemenu,FPImage,GraphType,IntfGraphics,LResources,lazbridge, opbitmapformats,RegisterLazOp;
// ----------------------------------------------

type
// ----------------------------------------------
  TPluto2SpriteManger = class;
  TPluto2DEngine      = class;
  TPluto2DSprite      = class;

// ----------------------------------------------
// Ereignisse                                   -
  { Wird beim zeichnen von einem Srpite ausgelöst
    Wird von der 2D Engine aufgefangen und weiter geleitet
    an den Jeweiligen SpriteManger
  }
  TPlutoOnDraw = procedure (Sprite:TPluto2DSprite) of Object;

  {
    Wird ausgelöst sobald eine Kollision stattgefunden hat
  }
  TPlutoOnCollision = procedure (Sprite1, Sprite2:TPluto2DSprite) of Object;

// ----------------------------------------------

// ----------------------------------------------
// TPluto2DSprite                               -
// ----------------------------------------------
  TPluto2DSprite = class
  private
    tmp:TBitmap;
    tmp2:TBitMap;
    SpriteMager:TPluto2SpriteManger;

    isBackup:Boolean;
    ox,oy,ow,oh:Integer;
  protected

  public
    b1:TCanvasOPBitmap;

    // Muss vom Spritemanger gesetzt werden
    OutCanvas:TCanvas;
    Buffer, BackupBuffer:TBitMap;
    id, name, typ:String;
    ImageList:TPlutoImageList;
    
    // Das bild was dieses Sprite da stellen soll.
    SpriteImage:TBitMap;

    // Muss vom User gesetzt werden
    Left,Top, Width, Height:Integer;
    beschreibung:string;
    NoCollision:Boolean;

    // Wenn gesetzt wird alles Tranzparent gezeichnet
    Tranzparent:Boolean;
    TranzparentColor:TColor;
    procedure DrawTrazparent;
    constructor Create(aSpriteManger:TPluto2SpriteManger);
    destructor Destroy; override;
    procedure LoadImage;
    procedure DrawSprite(const  event:Boolean = True);
    function GetR:TRect;
  published
  end; // TPluto2DSprite
  
// ----------------------------------------------
// TPluto2SpriteManger                          -
// ----------------------------------------------
  TPluto2SpriteManger  = class
    function GetSprite(index: Integer): TPluto2DSprite;
  private

  protected

  public
    Engine:TPluto2DEngine;
    fItems:TObjectList;
    fOnDraw:TPlutoOnDraw;
    onCollision:TPlutoOnCollision;
    property Items[index:Integer]:TPluto2DSprite read GetSprite;
    // Muss von TPluto2DEngine gesetzt werden
    OutCanvas:TCanvas;
    Buffer, BackupBuffer:TBitMap;
    ImageList:TPlutoImageList;
    
    // Gibt alle Änderungen auf dem Bildschrim aus
    procedure flip;
    procedure DrawAllSprite(Sprite:TPluto2DSprite);
    constructor Create(Pluto2DEngine:TPluto2DEngine);

    // Löst das Ereigniss onCollision aus
    procedure CheckCollision;overload;

    // Löst das Ereigniss onCollision aus
    procedure CheckCollision(Sprite:TPluto2DSprite);overload;

    // Löst das Ereigniss onCollision aus
    procedure CheckCollision(const ax,ay:Integer);

    function GetCollision(const ax,ay:Integer; sprite:TPluto2DSprite):Integer;

    destructor Destroy;

  published
  end; // TPluto2SpriteManger

// ----------------------------------------------
// TPluto2DEngine                               -
// ----------------------------------------------
  TPluto2DEngine = class
  private
  protected

  public
    b2:TCanvasOPBitmap;
    // Wenn es Menus Im Spiel gibt, sollten sie hier rein
    GameMenu:TPlutoGameMenu;
    
    // Enhält das Spiel Fehld als TRect
    GameFehld:TRect;
    // Enthält alle Manger die Geladen wurden
    SpriteMangerList:TObjectList;
    // Stellt ein ImageList zu verfügung
    ImageList:TPlutoImageList;
    // Muss vom User angeben werden
    OutCanvas:TCanvas;
    // Wird Automatisch gesetzt
    Buffer, BackupBuffer:TBitMap;

    // Wird von einem Sprite Manger ausgelöst, wenn es eine
    // oder mehrer Kollisionen geben hat
    onCollision:TPlutoOnCollision;
    
    // Enhält eine Liste von allen Level einer Mission
    LevelMissionen:TStringlist;
    // Enhält den Index des Aktullen Levels in der Mission
    LevelIndex:Integer;
    constructor Create;
    destructor Destroy; override;

    // Setzt Buffer, BackuBuffer und OutCanvas für einen Spritemanger
    procedure RegSpriteManger(var SpriteManger:TPluto2SpriteManger);
    // Gibt Buffer auf OutCanvas aus
    procedure flip;
    // Setzt Buffer, BackuBuffer und OutCanvas für einen Sprite
    procedure ReSprite(var Sprite:TPluto2DSprite);
    procedure SetSize(const aw, ah:Integer);
    procedure DrawSprits(Sprite:TPluto2DSprite);
    procedure CheckCollision;
    procedure CheckCollision(sprite:TPluto2DSprite);overload;
    procedure CheckCollision(const ax,ay:Integer);overload;
    function GetCollision(const ax,ay:Integer;sprite:TPluto2DSprite):Integer;
    procedure LoadMissionFile(const aFile:String);
  published
  end; // TPluto2DEngine

implementation

procedure DrawTransparentBitmap(DC: HDC; hBmp : HBITMAP ;
          xStart: integer; yStart : integer; cTransparentColor : COLORREF);
var
      bm:                                                  BITMAP;
      cColor:                                              COLORREF;
      bmAndBack, bmAndObject, bmAndMem, bmSave:            HBITMAP;
      bmBackOld, bmObjectOld, bmMemOld, bmSaveOld:         HBITMAP;
      hdcMem, hdcBack, hdcObject, hdcTemp, hdcSave:        HDC;
      ptSize:                                              TPOINT;

begin
   hdcTemp := CreateCompatibleDC(dc);
   SelectObject(hdcTemp, hBmp);   // Select the bitmap

   GetObject(hBmp, sizeof(BITMAP), @bm);
   ptSize.x := bm.bmWidth;            // Get width of bitmap
   ptSize.y := bm.bmHeight;           // Get height of bitmap
   DPtoLP(hdcTemp, ptSize, 1);        // Convert from device
                                      // to logical points

   // Create some DCs to hold temporary data.
   hdcBack   := CreateCompatibleDC(dc);
   hdcObject := CreateCompatibleDC(dc);
   hdcMem    := CreateCompatibleDC(dc);
   hdcSave   := CreateCompatibleDC(dc);

   // Create a bitmap for each DC. DCs are required for a number of
   // GDI functions.

   // Monochrome DC
   bmAndBack   := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil);

   // Monochrome DC
   bmAndObject := CreateBitmap(ptSize.x, ptSize.y, 1, 1, nil);

   bmAndMem    := CreateCompatibleBitmap(dc, ptSize.x, ptSize.y);
   bmSave      := CreateCompatibleBitmap(dc, ptSize.x, ptSize.y);

   // Each DC must select a bitmap object to store pixel data.
   bmBackOld   := SelectObject(hdcBack, bmAndBack);
   bmObjectOld := SelectObject(hdcObject, bmAndObject);
   bmMemOld    := SelectObject(hdcMem, bmAndMem);
   bmSaveOld   := SelectObject(hdcSave, bmSave);

   // Set proper mapping mode.
//   SetMapMode(hdcTemp, GetMapMode(dc));

   // Save the bitmap sent here, because it will be overwritten.
   BitBlt(hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);

   // Set the background color of the source DC to the color.
   // contained in the parts of the bitmap that should be transparent
   cColor := SetBkColor(hdcTemp, cTransparentColor);

   // Create the object mask for the bitmap by performing a BitBlt
   // from the source bitmap to a monochrome bitmap.
   BitBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
          SRCCOPY);

   // Set the background color of the source DC back to the original
   // color.
   SetBkColor(hdcTemp, cColor);

   // Create the inverse of the object mask.
   BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0,
          NOTSRCCOPY);

   // Copy the background of the main DC to the destination.
   BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, dc, xStart, yStart,
          SRCCOPY);

   // Mask out the places where the bitmap will be placed.
   BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);

   // Mask out the transparent colored pixels on the bitmap.
   BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);

   // XOR the bitmap with the background on the destination DC.
   BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);

   // Copy the destination to the screen.
   BitBlt(dc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0,
          SRCCOPY);

   // Place the original bitmap back into the bitmap sent here.
   BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0, SRCCOPY);

   // Delete the memory bitmaps.
   DeleteObject(SelectObject(hdcBack, bmBackOld));
   DeleteObject(SelectObject(hdcObject, bmObjectOld));
   DeleteObject(SelectObject(hdcMem, bmMemOld));
   DeleteObject(SelectObject(hdcSave, bmSaveOld));

   // Delete the memory DCs.
   DeleteDC(hdcMem);
   DeleteDC(hdcBack);
   DeleteDC(hdcObject);
   DeleteDC(hdcSave);
   DeleteDC(hdcTemp);
end;

// ----------------------------------------------
// ----------------------------------------------
procedure TPluto2DEngine.RegSpriteManger(
  var SpriteManger: TPluto2SpriteManger);
begin
  SpriteManger.OutCanvas:=OutCanvas;
  SpriteManger.Buffer:=buffer;
  SpriteManger.BackupBuffer:=BackupBuffer;
  SpriteManger.ImageList:=ImageList;
end;// RegSpriteManger

procedure TPluto2DEngine.flip;
var
  px,py,pw,ph:Integer;
  x,y:Integer;
begin
  Buffer.Canvas.Draw(0,0,BackupBuffer);
  px:=OutCanvas.ClipRect.Left;
  py:=OutCanvas.ClipRect.top;

  pw:=OutCanvas.ClipRect.Right-px;
  ph:=OutCanvas.ClipRect.Bottom-py;

  for y:=0 to SpriteMangerList.Count-1 do begin
    for x:=0 to TPluto2SpriteManger(SpriteMangerList[y]).fItems.Count-1 do begin
      TPluto2DSprite(TPluto2SpriteManger(SpriteMangerList[y]).fItems[x]).DrawSprite;
    end; // for x
  end; // for y
  OutCanvas.Draw(0,0,buffer);
//  BitBlt(OutCanvas.Handle,px,py,pw,ph,Buffer.Canvas.Handle,px,py,SRCCOPY);
end; // TPluto2SpriteManger.flip

procedure TPluto2DEngine.ReSprite(var Sprite: TPluto2DSprite);
begin
  Sprite.OutCanvas:=OutCanvas;
  Sprite.Buffer:=buffer;
  Sprite.BackupBuffer:=BackupBuffer;
end; // TPluto2DEngine.ReSprite

procedure TPluto2DEngine.SetSize(const aw, ah: Integer);
begin
  Buffer.Width:=aw; Buffer.Height:=ah;
  Buffer.Canvas.Brush.color:=clWhite;
  Buffer.Canvas.FillRect(0,0,aw,ah);

  BackupBuffer.Width:=aw; BackupBuffer.Height:=ah;
  BackupBuffer.Canvas.Brush.color:=clWhite;
  BackupBuffer.Canvas.FillRect(0,0,aw,ah);

end; // TPluto2DEngine.SetSize

// ----------------------------------------------
// TPluto2DEngine                               -
// ----------------------------------------------
constructor TPluto2DEngine.Create;
begin
  inherited Create;
  b2:=TCanvasOPBitmap.Create;
  b2.TransparentColor:=clwhite;
  SpriteMangerList:=TObjectList.Create;
  Buffer:=TBitMap.Create; BackupBuffer:=TBitMap.Create;
  ImageList:=TPlutoImageList.Create;
  LevelMissionen:=TStringlist.Create;
  LevelIndex:=0; GameMenu:=TPlutoGameMenu.Create;
  GameMenu.Buffer:=Buffer;  GameMenu.BackupBuffer:=BackupBuffer;

end; // TPluto2DEngine.Create

destructor TPluto2DEngine.Destroy;
begin
  FreeAndNil(ImageList);

  FreeAndNil(Buffer); FreeAndNil(BackupBuffer);

  inherited Destroy;
end; // TPluto2DEngine.Destroy

procedure TPluto2DEngine.DrawSprits(Sprite: TPluto2DSprite);
var
  i:Integer;
begin
  for i:=0 to SpriteMangerList.Count-1 do
    TPluto2SpriteManger(SpriteMangerList[i]).DrawAllSprite(Sprite);
end;

procedure TPluto2DEngine.CheckCollision;
var
  i:Integer;
begin
  for i:=0 to SpriteMangerList.Count-1 do
    TPluto2SpriteManger(SpriteMangerList[i]).CheckCollision
end; // TPluto2DEngine.CheckCollision

procedure TPluto2DEngine.CheckCollision(sprite: TPluto2DSprite);
var
  i:Integer;
begin
  for i:=0 to SpriteMangerList.Count-1 do
    TPluto2SpriteManger(SpriteMangerList[i]).CheckCollision(sprite);
end;

procedure TPluto2DEngine.CheckCollision(const ax, ay: Integer);
var
  i:Integer;
begin
  for i:=0 to SpriteMangerList.Count-1 do
    TPluto2SpriteManger(SpriteMangerList[i]).CheckCollision(ax,ay);
end;

function TPluto2DEngine.GetCollision(const ax, ay: Integer;sprite:TPluto2DSprite): Integer;
var
  i,z:Integer;
begin
  for i:=0 to SpriteMangerList.Count-1 do
    z:=TPluto2SpriteManger(SpriteMangerList[i]).GetCollision(ax,ay,sprite);

  result:=z;
end;

procedure TPluto2DEngine.LoadMissionFile(const aFile: String);
begin
  if FileExists(aFile+'missionen.txt') then begin
    LevelMissionen.LoadFromFile(aFile+'missionen.txt');
  end;
end;

function TPluto2SpriteManger.GetSprite(index: Integer): TPluto2DSprite;
begin
  result:=TPluto2DSprite(fItems[index]);
end; // TPluto2SpriteManger.GetSprit

procedure TPluto2SpriteManger.flip;
var
  px,py,pw,ph:Integer;
begin
  Buffer.Canvas.Draw(0,0,BackupBuffer);
  px:=OutCanvas.ClipRect.Left;
  py:=OutCanvas.ClipRect.top;
  
  pw:=OutCanvas.ClipRect.Right-px;
  ph:=OutCanvas.ClipRect.Bottom-py;


  BitBlt(OutCanvas.Handle,px,py,pw,ph,Buffer.Canvas.Handle,px,py,SRCCOPY);
end; // TPluto2SpriteManger.flip

procedure TPluto2SpriteManger.DrawAllSprite(Sprite:TPluto2DSprite);
var
  i:Integer;
  r:TRect;
begin
  for i:=0 to fItems.count-1 do begin
    if (items[i] <> NIL) and (items[i].name <> Sprite.name) and (IntersectRect(r,sprite.GetR,Items[i].GetR)) then begin
      Items[i].DrawSprite(False);
    end;
  end;

end;
 // TPluto2SpriteManger.DrawAllSprite
// ----------------------------------------------
// TPluto2SpriteManger                          -
// ----------------------------------------------
constructor TPluto2SpriteManger.Create(Pluto2DEngine:TPluto2DEngine);
begin
  inherited Create;

  OutCanvas:=Pluto2DEngine.OutCanvas;
  Buffer:=Pluto2DEngine.Buffer;
  BackupBuffer:=Pluto2DEngine.BackupBuffer;
  ImageList:=Pluto2DEngine.ImageList;
  fItems:=TObjectList.Create;
  fOnDraw:=@Pluto2DEngine.DrawSprits;
  Pluto2DEngine.SpriteMangerList.Add(self);
  Engine:=Pluto2DEngine;
end; // TPluto2SpriteManger.Create

procedure TPluto2SpriteManger.CheckCollision;
var
  i,x:Integer;
  r1,r2,r3:TRect;
begin
  for i:=0 to fItems.count-1 do begin
    r1:=Items[i].GetR;
    for x:=0 to fItems.count-1 do begin
      if (i <> x) and {(not items[x].NoCollision) and} (not items[i].NoCollision) then begin
        r2:=Items[x].GetR;
        if IntersectRect(r3,r1,r2) then begin

          if Assigned(Engine.onCollision) then
            Engine.onCollision(items[i],items[x]);
        end;
      end;
    end;
  end;
end; // TPluto2SpriteManger.CheckCollision

procedure TPluto2SpriteManger.CheckCollision(Sprite: TPluto2DSprite);
var
  i:Integer;
  r1,r2,r3:TRect;
begin
  r1:=Sprite.GetR;

  for i:=0 to fItems.count-1 do begin
    if (items[i].name <> Sprite.name) {and (not items[i].NoCollision)} then begin
      r2:=items[i].GetR;
      if (IntersectRect(r3,r1,r2)) then begin
        if Assigned(Engine.onCollision) then
         Engine.onCollision(Sprite,items[i]);
      end;
    end;
  end;
end;

procedure TPluto2SpriteManger.CheckCollision(const ax, ay: Integer);
var
  i:Integer;
  p:TPoint;
begin
  p:=Point(ax,ay);
  
  for i:=0 to fItems.count-1 do begin
    if {(not items[i].NoCollision) and} (PtInRect(items[i].GetR,p)) and (Assigned(Engine.onCollision)) then
      Engine.onCollision(items[i],nil);
  end;
end;

function TPluto2SpriteManger.GetCollision(const ax, ay: Integer; sprite:TPluto2DSprite): Integer;
var
  i,z:Integer;
  p:TPoint;
  R1,R2,R3:TRect;
begin
  p:=Point(ax,ay);
  z:=-1;
  r1:=Engine.GameFehld;
  
  if (PtInRect(r1,p)) then begin
    for i:=0 to fItems.count-1 do begin
      if {(items[i].name <> sprite.name) and}
         (PtInRect(items[i].GetR,p)) then begin
        if (not items[i].NoCollision) then z:=i;

        if Assigned(Engine.onCollision) then
          Engine.onCollision(items[i],sprite);
      end;
    end;
    result:=z;
  end
  else begin
    if Assigned(Engine.onCollision) then
      Engine.onCollision(sprite,nil);
    result:=1;
  end;
end;

destructor TPluto2SpriteManger.Destroy;
begin
  inherited Destroy;
end; // TPluto2SpriteManger.Destroy

// ----------------------------------------------
// TPluto2DSprite                               -
// ----------------------------------------------

procedure TPluto2DSprite.DrawTrazparent;
var
  x,y:Integer;
  r1,r2:TRect;
begin
  tmp.Canvas.Draw(0,0,SpriteImage);
  AssignBitmapToOpBitmap(tmp,b1);

  r1:=rect(0,0,Width,Height);
  r2:=rect(left,top,Width,Height);
  b1.Canvas.CopyRect(r1,SpriteMager.Engine.b2.Canvas,r2);
  b1.TransparentColor:=clwhite;

  AssignOpBitmapToBitmap(b1,tmp2);
  buffer.canvas.Draw(left,top,tmp2); OutCanvas.Draw(left,top,tmp2);
end;

constructor TPluto2DSprite.Create(aSpriteManger:TPluto2SpriteManger);
begin
  inherited Create; isBackup:=False;
  Tranzparent:=False; tmp:=TBitMap.Create; tmp2:=TBitMap.Create;
  b1:=TCanvasOPBitmap.Create;

  ox:=-1; oy:=-1; ow:=-1; oh:=-1;
  NoCollision:=False;
  
  // Damit der Sprit über all zugreiff auf den SprigManger hat
  SpriteMager:=aSpriteManger;

  // Weist einige Standart Variabeln zu
  // OutCanvas ist der Bereich wo das ganze Sichtbar wird
  // z.b. eine Paintbox
  OutCanvas:=SpriteMager.OutCanvas;
  
  // Das OFF-Scren um Flackern zu vermeiden
  Buffer:=SpriteMager.Buffer;
  
  // Speichert das Hintergrund Bild
  BackupBuffer:=SpriteMager.BackupBuffer;
  
  // Damit der Sprit zugrief auf die Imageliste der
  // 2D Engine hat, wo alle verfügbaren Objekte geladen sind.
  ImageList:=SpriteMager.ImageList;

  { Fügt den Sprite dem Spritmanger zu
    ist wichtig fürs zeichnen und für die Collisions
    Funktion
  }

  SpriteMager.fItems.Add(self)
end; // TPluto2DSprite.Create

destructor TPluto2DSprite.Destroy;
begin
  SpriteMager.fItems.Remove(self);
  b1.free; tmp.free; tmp2.free;

  inherited Destroy;
  self:=niL;
end; // TPluto2DSprite.Destroy

procedure TPluto2DSprite.LoadImage;
var
  ImageListItem:TPlutoImageListItem;
begin
  if id <> '' then begin
    ImageListItem:=ImageList.FindID(id);
    if ImageListItem <> NIL then begin
      SpriteImage:=ImageListItem.image;
      name:=ImageListItem.name;
      typ:=ImageListItem.typ;
      Width:=SpriteImage.Width;
      Height:=SpriteImage.Height;
      tmp.Width:=Width; tmp.Height:=Height;
      tmp2.Width:=tmp.Width; tmp2.Height:=tmp.Height;

    end;
  end
  else begin
    if SpriteImage <> NIL then begin
      isBackup:=False;
      BitBlt(SpriteImage.Canvas.Handle,left,top,Width,Height,BackupBuffer.Canvas.Handle,left,top,SRCCOPY);
      SpriteImage:=nil;
    end;
  end;
end; // LoadImage

procedure TPluto2DSprite.DrawSprite(const event:Boolean = True);
begin
  if (not Tranzparent) then begin
    {{
      Wenn schon was gezeichnet wurde steht isBackup auf True
      und löscht das Akteulel Bild im Buffer und im Outcanvas(z.b. Paintbox)
    }}
    if isBackup then begin
      BitBlt(Buffer.Canvas.Handle,ox,oy,ow,oh,BackupBuffer.Canvas.Handle,ox,oy,SRCCOPY);
      BitBlt(OutCanvas.Handle,ox,oy,ow,oh,Buffer.Canvas.Handle,ox,oy,SRCCOPY);
    end;

    {{
      Zeichnet das Eigentliche Bild an der Neuen Position
    }}

    if SpriteImage <> NIL then begin
      Buffer.Canvas.Draw(left,top,SpriteImage);
      OutCanvas.Draw(left,top,SpriteImage);
    end;
  end
  else begin
    DrawTrazparent;
  end;

  {
    Löst bei der 2D Engine ein Event aus,
    welches bei allen SpritManger
    alle Sprits neuzeichnet bzw. nur die
    die sich im bereich befinden.

    Der Parameter Event gibt an, ob dieses
    Ereigniss ausgelöst werden soll oder nicht,
    sonst würde es eine Endlos schleife geben.
  }
  
  if (SpriteMager <> NIL) and (Assigned(SpriteMager.fOnDraw)) and (event) then begin
    SpriteMager.fOnDraw(self);
  end;

  if SpriteImage <> NIL then begin
    // Speichert die Gerade gezeichnete Werte und stellt IsBackup auf True
    ox:=left; oy:=top; ow:=SpriteImage.Width; oh:=SpriteImage.Height; isBackup:=True;
  end;
end;

function TPluto2DSprite.GetR: TRect;
begin
  result:=rect(ox,oy,ox+ow,oy+oh);
end;

end.
