{*************************************************************************}
{                                                                         }
{ ObjectUnit.pas - Object inspector window                                }
{                                                                         }
{ This file is a part of the project:                                     }
{   Dizzy Quest Editor - the quest editor for Dizzy-like games            }
{                                                                         }
{ © Parfenov Alexey Andreevich, 2007                                      }
{                                                                         }
{ e-mail: AlkatrazStudio@yandex.ru                                        }
{                                                                         }
{ This program is free software; you can redistribute it and/or modify    }
{ it under the terms of the GNU General Public License as published by    }
{ the Free Software Foundation; either version 2 of the License, or       }
{ (at your option) any later version.                                     }
{                                                                         }
{ This program is distributed in the hope that it will be useful,         }
{ but WITHOUT ANY WARRANTY; without even the implied warranty of          }
{ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            }
{ GNU General Public License for more details.                            }
{                                                                         }
{ You may read GNU General Public License at:                             }
{   http://www.gnu.org/copyleft/gpl.html                                  }
{                                                                         }
{*************************************************************************}
unit ObjectUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, ValEdit, Grids, StdCtrls,dqe2_types,typinfo, ExtCtrls,
  GR32_Image,gr32,gr32_layers,math,jpeg, Menus, alkatraz;

type
  TForm_Objects = class(TForm)
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    v: TValueListEditor;
    ComboBox1: TComboBox;
    CheckBox1: TCheckBox;
    ComboBox2: TComboBox;
    ComboBox3: TComboBox;
    Label1: TLabel;
    Label2: TLabel;
    ComboBox4: TComboBox;
    SpriteScroll: TScrollBar;
    CheckBox2: TCheckBox;
    PaintBox: TImage32;
    Label3: TLabel;
    ComboBox5: TComboBox;
    CheckBox3: TCheckBox;
    CheckBox4: TCheckBox;
    StatusBar1: TStatusBar;
    ColorDialog1: TColorDialog;
    procedure vEditButtonClick(Sender: TObject);
    procedure ComboBox2Change(Sender: TObject);
    procedure UpdateZoneList;
    procedure UpdateList;
    procedure NameChanged;
    procedure CheckBox3Click(Sender: TObject);
    procedure ResetSelection;
    procedure PrepareScroll;
    procedure CalcXY(n:integer;var x:integer;var y:integer);
    function GetSelectedSprite(x,y:integer;var SelSprite:DQE2_Sprite):integer;
    procedure PaintSprites;
    function GetCurrentObject:DQE2_Object;
    procedure GenerateProperties;
    procedure FormResize(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
    procedure vValidate(Sender: TObject; ACol, ARow: Integer;
      const KeyName, KeyValue: String);
    procedure vSelectCell(Sender: TObject; ACol, ARow: Integer;
      var CanSelect: Boolean);
    procedure vKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure vStringsChange(Sender: TObject);
    procedure ComboBox3Change(Sender: TObject);
    procedure SpriteScrollChange(Sender: TObject);
    procedure ComboBox4Change(Sender: TObject);
    procedure TabSheet2Show(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject);
    procedure ComboBox1Change(Sender: TObject);
    procedure PaintBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer; Layer: TCustomLayer);
    procedure PaintBoxMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
    procedure ComboBox5Change(Sender: TObject);
    procedure vExit(Sender: TObject);
    procedure FormDeactivate(Sender: TObject);
    procedure Localize;
    procedure CheckBox4Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

const
  Spacing=4;

var
  Form_Objects: TForm_Objects;
  PickList:TList;
  CurObj:DQE2_Graphical;
  CurSprite,FocusSprite:DQE2_Sprite;
  OldValue:Variant;
  NowChanging:boolean=false;
  CurSiz:integer;
  CurList:TStringList;
  n_foc,n_cur,last_anim:integer;
  cur_pos:single;
  done:boolean=false;

implementation

uses GameUnit, MainUnit, EditDefaultsUnit, EditBlendUnit, ChooseUnit;

{$R *.dfm}

var lng:array of string;

procedure TForm_Objects.FormResize(Sender: TObject);
begin
  v.Height:=height-120;
  paintBox.Height:=height-145;
  SpriteScroll.Height:=height-145;
  PaintBox.Resize;PaintBox.SetupBitmap(true,clwhite32);
  PrepareScroll;
  PaintSprites;
end;

procedure TForm_Objects.FormCreate(Sender: TObject);
begin
  PickList:=TList.Create;
  ComboBox3.Items.AddStrings(world.ImageList);
  ComboBox3.ItemIndex:=0;
  PaintBox.SetupBitmap(true,clWhite32);
  PaintBox.Bitmap.DrawMode:=dmOpaque;
  PaintSprites;
  ComboBox3Change(nil);
  ComboBox4Change(nil);
  SpriteScrollChange(nil);
  CurObj:=nil;CurSprite:=nil;FocusSprite:=nil;
  cur_pos:=0;n_foc:=-1;n_cur:=-1;
  done:=true;
  ComboBox5.ItemIndex:=4;ComboBox5Change(nil);
  SetLength(lng,16);
end;

procedure TForm_Objects.FormDeactivate(Sender: TObject);
begin
  if v.ItemProps[v.Row].ReadOnly=false then
    vValidate(v,v.Col,v.Row,v.Keys[v.Row],v.Values[v.Keys[v.Row]]);
end;

procedure TForm_Objects.GenerateProperties;
var
  I, Count: Integer;
  PropInfo: PPropInfo;
  PropList: PPropList;
  tmp:DQE2_Dummy;
  ent:boolean;
begin
  NowChanging:=true;
  StatusBar1.Panels[0].Text:=Format(lng[13],[PickList.Count]);
  GetCurrentObject;
  if CurObj<>nil then
  begin
    v.Strings.Clear;
    v.Visible:=true;
    tmp:=CurObj;
    ComboBox1.ItemIndex:=ComboBox1.Items.IndexOfObject(CurObj);
    Count:=gettypedata(tmp.ClassInfo)^.PropCount;
    if Count>0 then
    begin
      getmem(PropList,Count*SizeOf(Pointer));
      try
        GetPropInfos(tmp.ClassInfo,PropList);
        for I := 0 to Count - 1 do
        begin
          PropInfo := PropList^[I];
          if PropInfo = nil then Break;
          if(propinfo^.Name='Zone')then continue;
          if IsStoredProp(tmp, PropInfo) then
          begin
            ent:=(propinfo^.Name='EntityType');
            if ent then
            begin
              v.InsertRow(propinfo^.Name,
                DQE2_EntityType(pointer(cardinal(GetPropValue(tmp,propinfo,true)))).Name,true);
              v.ItemProps[v.RowCount-1].ReadOnly:=true;
            end
            else
              v.InsertRow(propinfo^.Name,GetPropValue(tmp,propinfo,true),true);
            if(propinfo^.Name='DrawFX')or(propinfo^.Name='Color')or(ent)then
              v.ItemProps[v.RowCount-1].EditStyle:=esEllipsis
            else
              case PropInfo^.PropType^.Kind of
                tkEnumeration:
                  begin
                    v.ItemProps[v.RowCount-1].EditStyle:=esPickList;
                    v.ItemProps[v.RowCount-1].PickList.Add('False');
                    v.ItemProps[v.RowCount-1].PickList.Add('True');
                  end;
                else
                  v.ItemProps[v.RowCount-1].EditStyle:=esSimple;
            end;
            if PropInfo^.SetProc=nil then
              v.ItemProps[v.RowCount-1].ReadOnly:=true;
          end;
        end;
      finally
        FreeMem(proplist,Count * SizeOf(Pointer));
      end;
    end;
  end
  else
    v.Visible:=false;
  NowChanging:=false;
end;

procedure TForm_Objects.CheckBox1Click(Sender: TObject);
begin
  GetCurrentObject;
  UpdateList;
  GenerateProperties;
end;

procedure TForm_Objects.CheckBox3Click(Sender: TObject);
begin
  UpdateList;
end;

procedure TForm_Objects.CheckBox4Click(Sender: TObject);
begin
  if(MState=msNew)or(MState=msTile)then
    if CheckBox4.Checked then
      CurDrag.Angle:=random(360)
    else
      CurDrag.Angle:=0;
end;

procedure TForm_Objects.vValidate(Sender: TObject; ACol, ARow: Integer;
  const KeyName, KeyValue: String);
var
  a:integer;
  obj:DQE2_Object;
begin
  if Application.Terminated then exit;
  if MState=msNew then exit;
  NowChanging:=true;
  GetCurrentObject;
  if keyname='' then exit;
  if CurObj<>nil then
  begin
    for a:=0 to PickList.Count-1 do
    begin
      obj:=DQE2_Object(PickList[a]);
      try
        SetPropValue(Obj,KeyName,KeyValue);
        if KeyName='Name' then NameChanged;
      except
        if PickList.Count=1 then
        begin
          v.RestoreCurrentRow;
          SetPropValue(Obj,KeyName,v.Values[KeyName]);
        end;
      end;
      v.Values[KeyName]:=GetPropValue(Obj,KeyName);
    end;
  end;
  NowChanging:=false;
end;

function TForm_Objects.GetCurrentObject: DQE2_Object;
begin
  if PickList.Count>=1 then
    result:=DQE2_Object(PickList[0])
  else
    result:=nil;
  CurObj:=Result;
end;

procedure TForm_Objects.vSelectCell(Sender: TObject; ACol, ARow: Integer;
  var CanSelect: Boolean);
begin
  if Application.Terminated then exit;
  if CurObj=nil then exit;
  if v.Keys[ARow]<>'' then
    Oldvalue:=GetPropValue(CurObj,v.Keys[ARow]);
end;

procedure TForm_Objects.vEditButtonClick(Sender: TObject);
var
  et:DQE2_EntityType;
  a,n:integer;
begin
  if v.Keys[v.Row]='DrawFX' then
  begin
    Form_EditBlend.ShowModal;
  end else
  if v.Keys[v.Row]='Color' then
  begin
    if ColorDialog1.Execute then
      vValidate(v,v.Col,v.Row,v.Keys[v.Row],inttostr(ColorDialog1.Color));
  end else
  if v.Keys[v.Row]='EntityType' then
  begin
    if PickList.Count=1 then
      if DQE2_Entity(PickList[0]).EntityType<>nil then
        n:=DQE2_Entity(PickList[0]).EntityType.Index
      else
        n:=0
    else
      n:=0;
    if World.State<>wsStop then
      if MessageDlgRus(lng[14],lng[15],MB_OKCANCEL or MB_ICONWARNING)<>mrok then
        exit
      else
        World.Stop;
    et:=DQE2_EntityType(Form_Choose.ShowIt(World.EntityTypes,n));
    if et<>nil then
      for a:=0 to PickList.Count-1 do
        if DQE2_Dummy(PickList[a]) is DQE2_Entity then
          DQE2_Entity(PickList[a]).EntityType:=et;
  end;
end;

procedure TForm_Objects.vExit(Sender: TObject);
begin
  if Application.Terminated then exit;
  vValidate(v,v.Col,v.Row,v.Keys[v.Row],v.Values[v.Keys[v.Row]]);
end;

procedure TForm_Objects.vKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if key=13 then
    vValidate(v,v.Col,v.Row,v.Keys[v.Row],v.Values[v.Keys[v.Row]]);
end;

procedure TForm_Objects.vStringsChange(Sender: TObject);
begin
  if Application.Terminated then exit;
  if not nowchanging then
    if v.ItemProps[v.Row]<>nil then
      if v.ItemProps[v.Row].HasPickList then
        vValidate(v,v.Col,v.Row,v.Keys[v.Row],v.Values[v.Keys[v.Row]]);
end;

procedure TForm_Objects.PaintSprites;
var
  n,num,x,y:integer;
  bmp:TBitmap;
begin
  if (not done)or Application.Terminated then exit;
  PaintBox.Bitmap.Clear(clWhite32);
  if CurList=nil then exit else n:=CurList.Count;
  if n>0 then
  begin
    x:=0;num:=0;y:=-SpriteScroll.Position;
    repeat
      CalcXY(num,x,y);
        bmp:=DQE2_Sprite(CurList.Objects[num]).Thumbnail;
        PaintBox.Bitmap.Draw(rect(x+Spacing,y+Spacing,x+CurSiz-Spacing,y+CurSiz-Spacing),bmp.Canvas.ClipRect,bmp.Canvas.Handle);
      inc(num);
    until num=n;
    if FocusSprite<>nil then
    begin
      CalcXY(n_foc,x,y);
      PaintBox.Bitmap.FrameRectS(x+Spacing,y+Spacing,x+CurSiz-Spacing,y+CurSiz-Spacing,clblue32);
      PaintBox.Bitmap.FrameRectS(x,y,x+CurSiz,y+CurSiz,clblue32);
    end;
    if CurSprite<>nil then
    begin
      CalcXY(n_cur,x,y);
      PaintBox.Bitmap.FrameRectS(x+Spacing,y+Spacing,x+CurSiz-Spacing,y+CurSiz-Spacing,clred32);
      PaintBox.Bitmap.FrameRectS(x,y,x+CurSiz,y+CurSiz,clred32);
    end;
  end;
end;

procedure TForm_Objects.ComboBox3Change(Sender: TObject);
begin
  if ComboBox3.ItemIndex=-1 then CurList:=nil else
  begin
    CurList:=TStringList(world.ImageList.Objects[ComboBox3.ItemIndex]);
    SpriteScroll.Position:=0;
    PrepareScroll;
    PaintSprites;
  end;
  ResetSelection;
end;

procedure TForm_Objects.SpriteScrollChange(Sender: TObject);
begin
  if CurList=nil then exit;
  PaintSprites;
end;

function TForm_Objects.GetSelectedSprite(x, y: integer;
  var SelSprite:DQE2_Sprite): integer;
begin
  SelSprite:=nil;result:=-1;
  if CurList<>nil then
    if(x>0)and(y>0)and(x<PaintBox.Width)and(y<PaintBox.Height)then
    begin
      y:=y+SpriteScroll.Position;
      result:=trunc(y/CurSiz)*(ComboBox4.ItemIndex+1)+trunc(x/CurSiz);
      if result<CurList.Count then
        SelSprite:=DQE2_Sprite(CurList.Objects[result])
      else
        result:=-1;
    end;
end;

procedure TForm_Objects.Localize;
var
  n:integer;
begin
  LoadStringsFromIni(MainUnit.lang_ini,Name,lng);
  caption:=lng[0];
  TabSheet1.Caption:=lng[1];
  Label1.Caption:=lng[2];
  Label2.Caption:=lng[3];
  n:=ComboBox4.ItemIndex;
  ComboBox4.Items[0]:=lng[4];
  ComboBox4.Items[1]:=lng[5];
  ComboBox4.Items[2]:=lng[6];
  ComboBox4.ItemIndex:=n;
  CheckBox2.Caption:=lng[7];
  CheckBox4.Caption:=lng[8];
  Label3.Caption:=lng[9];
  TabSheet2.Caption:=lng[10];
  CheckBox1.Caption:=lng[11];
  CheckBox3.Caption:=lng[12];
end;

procedure TForm_Objects.NameChanged;
var
  x:integer;
begin
  x:=ComboBox1.ItemIndex;
  UpdateList;
  if x>=ComboBox1.Items.Count then
    ComboBox1.ItemIndex:=-1
  else
    ComboBox1.ItemIndex:=x;
end;

procedure TForm_Objects.ComboBox4Change(Sender: TObject);
begin
  CurSiz:=trunc(PaintBox.Width div(ComboBox4.ItemIndex+1));
  PrepareScroll;
  PaintSprites;
end;

procedure TForm_Objects.CalcXY(n: integer; var x, y: integer);
var
  sc:integer;
begin
  sc:=ComboBox4.ItemIndex+1;
  y:=trunc(n/sc)*CurSiz-SpriteScroll.Position+1;x:=(n mod sc)*CurSiz;
end;

procedure TForm_Objects.PrepareScroll;
var
  ofs:integer;
begin
  if CurList=nil then exit;
  Ofs:=trunc(CurSiz*(CurList.Count/(ComboBox4.ItemIndex+1)+0.75)-PaintBox.Height);
  if ofs<1 then ofs:=1;
  SpriteScroll.Max:=ofs;
end;

procedure TForm_Objects.ResetSelection;
begin
  FocusSprite:=nil;
  CurSprite:=nil;
  PaintSprites;
end;

procedure TForm_Objects.TabSheet2Show(Sender: TObject);
begin
  GenerateProperties;
end;

procedure TForm_Objects.UpdateList;
var
  a:integer;

  procedure AddFromZone(zn:dqe2_Zone);
  var
    a:integer;
    obj:DQE2_Object;
  begin
    for a:=0 to zn.Objects.Count-1 do
    begin
      obj:=dqe2_object(zn.Objects[a]);
      if(obj.Marked)or(not CheckBox3.Checked) then
        ComboBox1.Items.AddObject(obj.Name,obj);
    end;
  end;

begin
  ComboBox1.Items.Clear;
  if(CheckBox1.Checked)and(ComboBox2.ItemIndex<>-1)then
    AddFromZone(DQE2_Zone(World.Zones[ComboBox2.ItemIndex]))
  else
    for a:=0 to World.Zones.Count-1 do
      AddFromZone(DQE2_Zone(World.Zones[a]));
end;

procedure TForm_Objects.UpdateZoneList;
var
  a:integer;
  zn:dqe2_zone;
begin
  ComboBox2.Clear;
  for a:=0 to World.Zones.count-1 do
  begin
    zn:=dqe2_zone(world.Zones[a]);
    ComboBox2.AddItem(zn.name,zn);
  end;
  ComboBox2.ItemIndex:=world.CurrentZone.Index;
end;

procedure TForm_Objects.PaintBox1Paint(Sender: TObject);
begin
  PaintSprites;
end;

procedure TForm_Objects.ComboBox1Change(Sender: TObject);
var
  obj:DQE2_Graphical;
begin
  if nowchanging then exit;
  PickList.Clear;
  obj:=DQE2_Graphical(ComboBox1.Items.Objects[ComboBox1.ItemIndex]);
  if obj.ClassNameIs('DQE2_Object') then
  begin
    if dqe2_Object(obj).Zone=World.CurrentZone then
      Form_Game.PickIt(obj);
  end
  else
    Form_Game.PickIt(obj);
  GenerateProperties;
end;

procedure TForm_Objects.ComboBox2Change(Sender: TObject);
begin
  if CheckBox1.Checked then UpdateList;
end;

procedure TForm_Objects.PaintBoxMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer; Layer: TCustomLayer);
var
  s:dqe2_sprite;
begin
  n_foc:=GetSelectedSprite(x,y,FocusSprite);
  if n_foc<>-1 then
  begin
    s:=DQE2_Sprite(CurList.Objects[n_foc]);
    StatusBar1.Panels[0].Text:=s.ShortName+' ('+
      inttostr(s.Width)+'x'+
      inttostr(s.Height)+')';
  end else StatusBar1.Panels[0].Text:='';
  PaintSprites;
end;

procedure TForm_Objects.PaintBoxMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer;
  Layer: TCustomLayer);
begin
  if FocusSprite<>nil then
  begin
    Form_Game.ResetMouse;
    CurSprite:=FocusSprite;
    n_cur:=n_foc;
    if ssright in shift then
    begin
      Form_EditDefaults.Edit1.Text:=CurSprite.DefaultName;
      Form_EditDefaults.CheckBox2.Checked:=CurSprite.DefaultTransparent;
      Form_EditDefaults.CheckBox1.Checked:=CurSprite.DefaultCollider;
      Form_EditDefaults.SpinEdit1.Value:=CurSprite.DefaultAnimSpeed;
      Form_EditDefaults.SpinEdit2.Value:=CurSprite.DefaultAlpha;
      Form_EditDefaults.ShowModal;
      if Form_EditDefaults.ok then
      begin
        CurSprite.DefaultName:=Form_EditDefaults.Edit1.Text;
        CurSprite.DefaultTransparent:=Form_EditDefaults.CheckBox2.Checked;
        CurSprite.Prepare(CurSprite.Name);
        CurSprite.DefaultCollider:=Form_EditDefaults.CheckBox1.Checked;
        CurSprite.DefaultAnimSpeed:=Form_EditDefaults.SpinEdit1.Value;
        CurSprite.DefaultAlpha:=Form_EditDefaults.SpinEdit2.Value;
      end;
    end
    else
    begin
      MState:=msNew;
      CurDrag.Sprite:=CurSprite;
      CurDrag.Name:=CurSprite.DefaultName;
      CurDrag.X:=World.OffsetX;CurDrag.Y:=World.OffsetY;
      if CheckBox4.Checked then
        CurDrag.Angle:=random(360);
      PickList.Clear;PickList.Add(CurDrag);
    end;
  end;
end;

procedure TForm_Objects.ComboBox5Change(Sender: TObject);
begin
  spd:=ComboBox5.ItemIndex+1;
end;

end.
