
{
 Nomenklatura:
  object: istniejący, naniesiony obiekt
  item:   obiekt z listy możliwych rzeczy
}

unit MainForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ImgList, ComCtrls, ToolWin, Grids, ValEdit, ExtCtrls, IniFIles,
  Menus, StdCtrls, Math, CTypes, ExtDlgs, janXmlParser2, XPMan, GraphicEx,
  ShellApi;

type
  TMainFrm = class(TForm)
    ToolBar1: TToolBar;
    NewFileBtn: TToolButton;
    ImageList1: TImageList;
    SaveFileBtn: TToolButton;
    ObjSelectRd: TToolButton;
    ObjAddRd: TToolButton;
    ObjDelRd: TToolButton;
    ToolButton7: TToolButton;
    Splitter1: TSplitter;
    Panel1: TPanel;
    ItemPreview: TImage;
    ItemSelector: TTreeView;
    Splitter2: TSplitter;
    RefreshBtn: TToolButton;
    ToolButton9: TToolButton;
    Scroller: TScrollBox;
    DrawBox: TImage;
    InfoBar: TStatusBar;
    MainMenu1: TMainMenu;
    Plik1: TMenuItem;
    Edycja1: TMenuItem;
    Widok1: TMenuItem;
    Nowy1: TMenuItem;
    Otwrz1: TMenuItem;
    Zapisz1: TMenuItem;
    Zapiszjako1: TMenuItem;
    N1: TMenuItem;
    DoGameEditora1: TMenuItem;
    N2: TMenuItem;
    Zakocz1: TMenuItem;
    Cofnij1: TMenuItem;
    Ponw1: TMenuItem;
    Odswie1: TMenuItem;
    Powikszenie1: TMenuItem;
    Zwiksz1: TMenuItem;
    Zmniejsz1: TMenuItem;
    Ustaw1: TMenuItem;
    FileOpen: TOpenDialog;
    FileSave: TSaveDialog;
    OpenFileBtn: TToolButton;
    Eksportujdoplikugry1: TMenuItem;
    ZoneSelRd: TToolButton;
    PropertiesPanel: TPanel;
    ObjectProperties: TStringGrid;
    PropPopup: TPopupMenu;
    Nowawaciwo1: TMenuItem;
    Usuzaznwaciwo1: TMenuItem;
    Panel3: TPanel;
    PropsConfirmBtn: TButton;
    ZoneShape: TShape;
    Panel4: TPanel;
    PosEdt: TEdit;
    DirEdt: TEdit;
    CatEdt: TEdit;
    ChPosRd: TToolButton;
    ChDirRd: TToolButton;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    FullViewBtn: TToolButton;
    ToolButton1: TToolButton;
    Panel5: TPanel;
    ZoomTrack: TScrollBar;
    Label4: TLabel;
    N4: TMenuItem;
    Wstawwaciwoci1: TMenuItem;
    Splitter3: TSplitter;
    OptPropsLst: TListBox;
    Mapa1: TMenuItem;
    Zaadujrelief1: TMenuItem;
    ReliefOpen: TOpenPictureDialog;
    Importujzplikugry1: TMenuItem;
    SceneFileOpen: TOpenDialog;
    DelAllBtn: TToolButton;
    N3: TMenuItem;
    Pokalog1: TMenuItem;
    Splitter4: TSplitter;
    XPManifest1: TXPManifest;
    Oprogramie1: TMenuItem;
    N5: TMenuItem;
    Restartprogramu1: TMenuItem;
    procedure RefreshBtnClick(Sender: TObject);
    procedure ItemSelectorChange(Sender: TObject; Node: TTreeNode);
    procedure DrawBoxMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormCreate(Sender: TObject);
    procedure RefreshObjectInfo;
    procedure UpdateMap;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Ustaw1Click(Sender: TObject);
    procedure Odswie1Click(Sender: TObject);
    procedure Zwiksz1Click(Sender: TObject);
    procedure Zmniejsz1Click(Sender: TObject);
//    procedure UpdateSize;
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure ShowPleaseWait(text:string; cl:TColor);
    procedure HidePleaseWait;
    procedure Eksportujdoplikugry1Click(Sender: TObject);
    procedure Nowawaciwo1Click(Sender: TObject);
    procedure Usuzaznwaciwo1Click(Sender: TObject);
    procedure PropsConfirmBtnClick(Sender: TObject);
    procedure OpenFileBtnClick(Sender: TObject);
    procedure SaveFileBtnClick(Sender: TObject);
    procedure DrawBoxMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure DrawBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FullViewBtnClick(Sender: TObject);
    procedure ZoomTrackChange(Sender: TObject);
    procedure OptPropsLstDblClick(Sender: TObject);
    procedure Zaadujrelief1Click(Sender: TObject);
    procedure Importujzplikugry1Click(Sender: TObject);
    procedure DelAllBtnClick(Sender: TObject);
    procedure Standardowe1Click(Sender: TObject);
    procedure Pokalog1Click(Sender: TObject);
    procedure Zakocz1Click(Sender: TObject);
    procedure Oprogramie1Click(Sender: TObject);
    procedure Restartprogramu1Click(Sender: TObject);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure ScrollerResize(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainFrm: TMainFrm;
  tf:TIniFile;
  TotalObjects:word=0;
  SelectedObject:TCObject;
  PxPerMetre:real=1;
  ClickTolerance:byte;
  Map:TCanvas;
  VzX:real=500;
  VzY:real=500;
  PWText:string;
  ShownRect: TFloatRect;
  AllObjects: TList;
  ZoneStart: TPoint;
  relief:TBitmap;
  ReliefFile:string;
  QuotedParams:TStringList;
  PicNothing, PicNoPhoto: TPicture;
  cind:^DWORD;
  CursorMapPos: TObjPos;
  CursorPointPos: TPoint;
  movingview: boolean=false;
  movingstart: TPoint;

implementation

uses  OutputForm, StrUtils, LoadingForm, DateUtils, AboutForm;

procedure ShowProgress(text:string; addtolog:bool=true);
begin
 with LoadingFrm do begin
  ProgressLbl.Caption := text;
  Show;
  if addtolog then begin
   LogRich.SelAttributes.Color := clBlue;
   LogRich.SelText := #10+TimeToStr(Now)+','+
    AddLeading(IntToStr(MilliSecondOf(Now)),3)+':  ';
   LogRich.SelAttributes.Color := clBlack;
   LogRich.SelText := text;
   LogRich.Perform(WM_VSCROLL,SB_LINEDOWN,0);
  end;
  Application.ProcessMessages;
 end;
 //Writeln(TimeToStr(Now)+','+
 //   AddLeading(IntToStr(MilliSecondOf(Now)),3)+':  '+text);
 //Sleep(Random(10)*10);
end;
procedure HideProgress;
begin
 LoadingFrm.Hide;
end;

procedure TMainFrm.ShowPleaseWait(text:string; cl:TColor);
begin
 InfoBar.Color := cl;
 InfoBar.Panels[2].Text := 'Proszę zaczekaj...';
 InfoBar.Panels[3].Bevel := pbRaised;
 InfoBar.Panels[3].Text := text;
 PWText := text;
 Application.ProcessMessages;
end;

procedure TMainFrm.HidePleaseWait;
begin
 InfoBar.Panels[2].Text := 'Zaznaczony objekt:';
 InfoBar.Panels[3].Bevel := pbLowered;
 if InfoBar.Panels[3].Text=PWText then
  InfoBar.Panels[3].Text := '---';
 InfoBar.Color := clBtnFace;
 RefreshObjectInfo;
end;

procedure ReportError(E: Exception; event:string='?'; action:string='?');
begin
   ShowProgress('----------------B Ł Ą D!----------------');
   ShowProgress('Event: '+event+', Action: '+action);
   ShowProgress('Internal msg: '+E.Message);
   ShowProgress('------------koniec komunikatu-----------');
   ShowProgress('Błąd', false);
   Application.MessageBox(
    'Błąd wewnętrzny. Raportując go, proszę załączyć log od linii "---BŁĄD---" do linii "---koniec komunikatu---".',
    'Maps Editor error', MB_ICONERROR);
end;

function MetresToPx(Metre:real):Integer; overload;
//var ts: LongWord;
begin
 Result := Round(Metre * PxPerMetre);
end;

function PxToMetres(Pixels:Integer):real; overload;
begin
 Result := (Pixels / PxPerMetre);
end;

function PxToMetres(Pixels:Integer; VZ:real):real; overload;
begin
 Result := (Pixels / PxPerMetre) + (VZ - PxToMetres(500));
end;
{
function PxToRoundMetres(Pixels:Integer):Integer;
begin
 Result := Round(Pixels / PxPerMetre);
end;
 }
function MetresToPx(Metre:real; VZ:real):Integer; overload;
//var ts: LongWord;
begin
 Result := Round((Metre-(VZ-PxToMetres(500))) * PxPerMetre);
end;

function CompareWithTolerance(param1, param2, tolerance: real):bool;
var dif:real;
begin
 dif := param1-param2;
 if dif<0 then dif := -dif;
 if (dif<=tolerance) then Result := true else Result := false;
end;


procedure WriteProperty(name, value:string; obj:TCObject); overload;
begin
 obj.Props.Values[name] := value;
end;

procedure WriteProperty(name, value:string); overload;
begin
 WriteProperty(name, value, SelectedObject);
end;

function ReadProperty(name, default:string; obj:TCObject):string; overload;
//var r:string;
begin
 Result := obj.Props.Values[name];
 if Result='' then Result := default;
end;

function ReadProperty(name, default:string):string; overload;
begin
 ReadProperty(name,default, SelectedObject);
end;

procedure Circle(Canvas:TCanvas; X, Y, R:integer);
begin
 Canvas.Ellipse(X-R, Y-R, X+R, Y+R);
end;

function ObjPosToPoint(pos:TObjPos):TPoint;
begin
 Result.X := MetresToPx(pos.x-ShownRect.Left);
 Result.Y := MetresToPx((-pos.y)-ShownRect.Top);
end;

function PointToObjPos(pnt:TPoint):TObjPos;
begin
 Result.x := PxToMetres(pnt.X)+ShownRect.Left;
 Result.y := -(PxToMetres(pnt.Y)+ShownRect.Top);
end;

{$R *.dfm}

procedure TMainFrm.RefreshObjectInfo;
var i:integer; f:TIniFile; oname:string; s:TStringList; op:TObjProp;
begin
 if not Assigned(SelectedObject) then begin
  PropertiesPanel.Enabled := false;
  exit;
 end;
 PropertiesPanel.Enabled := true;
 ItemPreview.Picture := SelectedObject.category.Photo;
 InfoBar.Panels.Items[3].Text := SelectedObject.category.LongName;
 for i := 1 to ObjectProperties.RowCount-1 do ObjectProperties.Rows[i].Clear; //czyszczenie tabeli
 ObjectProperties.RowCount := SelectedObject.Props.Count+1;
 //if SelectedObject.Props.Count>0 then
 for i := 0 to SelectedObject.Props.Count-1 do begin
  op := TObjProp(SelectedObject.Props.Items[i]^);
  //ShowMessage(op.Name+'='+op.Value);
  ObjectProperties.Cells[0,i+1] := op.Name;
  ObjectProperties.Cells[1,i+1] := op.Value;
 end;
 if ObjectProperties.RowCount>1 then ObjectProperties.FixedRows := 1;
 OptPropsLst.Items.Clear;
 for i := 0 to SelectedObject.category.OptProps.Count-1 do begin
  op := TObjProp(SelectedObject.category.OptProps.Items[i]^);
  OptPropsLst.Items.Add(op.Name+' = '+op.Value);
 end;
 PosEdt.Text := ObjPosToStr(SelectedObject.position);
 DirEdt.Text := FloatToStrF(SelectedObject.orientation,ffFixed,5,2);
 CatEdt.Text := SelectedObject.category.CatName;
end;

function RadToDir(rad:single):single;
begin
 Result := 2-(rad/Pi);
end;

function DirToRad(dir:single):single;
begin
 Result := -dir*Pi;
end;


///////////////////////////////////////////////////////////////////////////
///////  TMainFrm.UpdateMap
///////////////////////////////////////////////////////////////////////////
procedure TMainFrm.UpdateMap;
var c:TColor; oname:string;
    i:integer;
    b,bg:TPicture;
    size, rdir:single;
    mr:integer;
    t: TItemType;
    co: TCObject;
    pt:TPoint;
    pos:TObjPos;
begin
 try
  ShowPleaseWait('Odświeżanie mapy', clRed);
  InfoBar.Panels[1].Text := FloatToStrF(PxPerMetre,ffFixed,5,2)+' px/m';
  ZoomTrack.Position := Round(PxPerMetre*100);
  //DrawBox.Width := abs(MetresToPx(ShownRect.Right-ShownRect.Left));
  //DrawBox.Height := abs(MetresToPx(ShownRect.Bottom-ShownRect.Top));
  DrawBox.Picture.Bitmap.Height := DrawBox.Height;
  DrawBox.Picture.Bitmap.Width := DrawBox.Width;
  DrawBox.Canvas.Brush.Color := rgb(240,120,0);
  DrawBox.Canvas.FillRect(DrawBox.ClientRect);
  DrawBox.Canvas.CopyRect(DrawBox.ClientRect, relief.Canvas, Rect(
   Round((ShownRect.Left*161)/800)+80, (Round((ShownRect.Top*161)/800)+80),
   Round((ShownRect.Right*161)/800)+80, (Round((ShownRect.Bottom*161)/800)+80)));
  //bg.Free;

  b := TPicture.Create;
  for i := 0 to AllObjects.Count-1 do begin
   co := TCObject(AllObjects.Items[i]);
   pt := ObjPosToPoint(co.position);
   if (pt.x<0) or (pt.y<0) or (pt.x>DrawBox.Width) or (pt.y>DrawBox.Height) then continue;
   if not Assigned(co.category) then continue;
   oname := co.category.CatName;
   if oname='null' then continue;
   //if ReadProperty('type', 'null', i)='null' then continue;
   size := co.category.Radius;
   case co.category.ItemType of
    itBuilding: c := clYellow;
    itBot: c := clBlue;
    itAlien: c := clLime;
    itPortable: c := clRed;
    itDecor: c := clFuchsia;
    else c := clBlack;
   end;
   DrawBox.Canvas.Pen.Color := c;
   DrawBox.Canvas.Brush.Style := bsClear;
   Circle(DrawBox.Canvas, pt.x, pt.y, MetresToPx(size));
   if co=SelectedObject then begin
    DrawBox.Canvas.Pen.Color := clWhite;
    Circle(DrawBox.Canvas, pt.x, pt.y, MetresToPx(size/2));
   end;
   DrawBox.Canvas.Pen.Color := c;
   DrawBox.Canvas.MoveTo(pt.x, pt.y);
   rdir := DirToRad(co.orientation);
   pos.x := cos(rdir)*size+co.position.x;
   pos.y := sin(rdir)*size+co.position.y;
   pt := ObjPosToPoint(pos);
   DrawBox.Canvas.LineTo(pt.x,pt.y);
  end;
  b.Free;
  if AllObjects.Count=0 then SelectedObject := nil;
  HidePleaseWait;
 except
  on E: Exception do ReportError(E, 'n/a', 'Updating map');
 end;
end;

procedure TMainFrm.RefreshBtnClick(Sender: TObject);
begin
 UpdateMap;
end;

procedure TMainFrm.ItemSelectorChange(Sender: TObject; Node: TTreeNode);
var f:TIniFile; oname,pname:string; s:TStrings; i:word; cat:TCCat;
begin
 cat := Node.Data;
 if Assigned(cat) then
  ItemPreview.Picture := cat.Photo else
  ItemPreview.Picture := PicNothing;
end;

procedure TMainFrm.DrawBoxMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var oname, pname:string;
 i:integer; f:TIniFile; s:TStringList; dist:real;
 mindist:real; mdistobj:TCObject; // najmniejsza długość, najmniejszej długości obiekt
 cat:TCCat;
 prop:TObjProp;
 pos,pos2:TObjPos;
 r:single;
 obj:TCObject;
 action:string;
begin
 {x := x+VisibleZoneX;
 y := y+VisibleZoneY;}
 // Jesli dodawanie itemu:
 if Button=mbLeft then try
  ShowPleaseWait('Myślenie...', clAqua);
  if ObjAddRd.Down then begin
   action := 'add';
   // Pozyskaj nazwę wewnętrzną
   cat := ItemSelector.Selected.Data;
   if not Assigned(cat) then exit;
   oname := cat.CatName;
   SelectedObject := TCObject.Create;
   AllObjects.Add(SelectedObject);
   cat := AllCats.Cats[oname];
   SelectedObject.category := cat;
   SelectedObject.Props := TPropsList.Create;
   // Przepisywanie parametrów. Zastanów się, dlaczego nie można wykorzystać Assign :P
   if cat.Props.Count>0 then for i := 0 to cat.Props.Count-1 do begin
    prop := TObjProp(cat.Props.Items[i]^);
    SelectedObject.Props.Values[prop.Name] := prop.Value;
   end;
   // DODAWANIE PARAMETRÓW "UNIWERSALNYCH" ///////////////////////////////////
   SelectedObject.position := PointToObjPos(Point(x,y));
   RefreshObjectInfo;
   //UpdateMap;
  end;

  // Jesli zaznaczenie lub usunięcie ...
  if ObjSelectRd.Down or ObjDelRd.Down then begin
   action := 'selection';
   // ... trzeba najpierw sprawdzic co to
   mindist := 10000;
   pos := PointToObjPos(Point(x,y));
   for i := 0 to AllObjects.Count-1 do begin
    obj := TCObject(AllObjects.Items[i]);
    // Jesli usunięty lub nie ma, pomiń
    if not Assigned(obj.category) then continue;
    // Porównaj (Pitagoras twierdzi, że jak się pierdzi, to śmierdzi)
    dist := Sqrt(Power(obj.position.x-pos.x, 2)+Power(obj.position.y-pos.y, 2));
    if dist<mindist then begin
     mindist := dist;
     mdistobj := obj;
    end;
    SelectedObject := mdistobj;
    RefreshObjectInfo;
   end;
  end;
  if ObjDelRd.Down then begin
   action := 'removal';
   AllObjects.Delete(AllObjects.IndexOf(SelectedObject));
   SelectedObject.Free;
   SelectedObject := nil;
   //UpdateMap;
  end;

  if ZoneSelRd.Down then begin
   action := 'zone selection';
   ZoneShape.Visible := false;
   pos := PointToObjPos(ZoneStart);
   pos2 := PointToObjPos(Point(x,y));
   ShownRect.Left := pos.x;
   ShownRect.Top := -pos.y;
   ShownRect.Right := pos2.x;
   ShownRect.Bottom := -pos2.y;
   //UpdateMap;
  end;

  if ChPosRd.Down then begin
   action := 'position change';
   SelectedObject.position := PointToObjPos(Point(x,y));
   //UpdateMap;
   RefreshObjectInfo;
  end;
  if ChDirRd.Down then begin
   action := 'orientation change';
   pos := PointToObjPos(Point(x,y));                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    //
   pos2 := SelectedObject.position;
   if (pos.x=pos2.x) and (pos.y>pos2.y) then r := 1.5 else
   if (pos.x=pos2.x) and (pos.y<=pos2.y) then r := 0.5 else
   if (pos.x>pos2.x) and (pos.y=pos2.y) then r := 0 else
   if (pos.x<=pos2.x) and (pos.y=pos2.y) then r := 1 else
   if (pos.x>pos2.x) and (pos.y>pos2.y) then //   I ćwiartka
    r := 2-arctan((pos.y-pos2.y)/(pos.x-pos2.x))/Pi else
   if (pos.x<pos2.x) and (pos.y>pos2.y) then //  II ćwiartka
    r := -arctan((pos2.y-pos.y)/(pos2.x-pos.x))/Pi+1 else
   if (pos.x<pos2.x) and (pos.y<pos2.y) then // III ćwiartka
    r := 1-arctan((pos2.y-pos.y)/(pos2.x-pos.x))/Pi else
   if (pos.x>pos2.x) and (pos.y<pos2.y) then //  IV ćwiartka
   r := -arctan((pos.y-pos2.y)/(pos.x-pos2.x))/Pi;
   SelectedObject.orientation := r;
   //UpdateMap;
   RefreshObjectInfo;
  end;
  UpdateMap;
 except
  on E: Exception do ReportError(E, 'DrawBoxMouseUp', action);
 end else if Button=mbMiddle then movingview := false;
end;




//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////  LOADING-RELATED PROCEDURES
//////////////////////////////////////////////////////////////////////////////


procedure Explode(List:TStringList; seperator:char; text:string);
var i:integer;s:string; wasdiv:bool;
begin
 //wasdiv := false;
 for i := 1 to Length(text) do begin
  if text[i]=seperator then begin
   List.Add(s);
   s := '';
   //wasdiv := true;
  end else s := s+text[i];
 end;
 if s<>'' then
  List.Add(s);
end;

procedure ReadPropsFromNode(node:TjanXMLNode2; Props, OProps:TPropsList; IncNode:TjanXMLNode2);
var sl:TStringList; i:integer; s:string; plst:TPropsList; snode:TjanXMLNode2;
begin
 try
 //sl := TStringList.Create;
 //f.ReadSection(Section, sl);
 for i := 0 to node.nodes.Count-1 do begin
  snode := TjanXMLNode2(node.nodes[i]);
  //s := sl[i];
  if snode.name='ApplyFor' then continue;
  if snode.name='Optional' then plst := OProps;
  if snode.name='Property' then plst := Props;
  if snode.name='Include' then begin
   ReadPropsFromNode(
    IncNode.getChildByName(snode.attribute['name']), Props, OProps, IncNode);
   continue;
  end;
  if (snode.attribute['quoted']='1') and
  (QuotedParams.IndexOf(snode.attribute['name'])<0) then
  QuotedParams.Add(snode.attribute['name']);
  plst.Values[snode.attribute['name']] := snode.attribute['value'];
 end;
 except
  ShowMessage('Blad podczas przetwarzania: '+snode.attribute['name']);
 end;
end;

procedure ReadMenuNodesFromXml(MenuNode:TTreeNode; MenuNodes:TTreeNodes;
 XmlNode:TjanXMLNode2);
var i:integer; node: TjanXMLNode2; menuitem:TTreeNode;
begin
  for i := 0 to XmlNode.nodes.Count-1 do begin
   node := XmlNode.nodes[i];
   if node.name='Item' then begin
    try
     ShowProgress('Tworzenie elementu: "'+node.attribute['name']+'"', false);
     menuitem :=
      MenuNodes.AddChild(MenuNode, AllCats.Cats[node.attribute['name']].LongName);
     menuitem.Data := AllCats.Cats[node.attribute['name']];
    except
     on E: Exception do ReportError(E, 'FormCreate',
      'Creating items list element named: '+node.attribute['name']);
    end;
   end;
   if node.name='Node' then begin
    ShowProgress('Tworzenie gałęzi: "'+node.attribute['title']+'"...');
    menuitem := MenuNodes.AddChild(MenuNode, node.attribute['title']);
    ReadMenuNodesFromXml(menuitem, MenuNodes, node);
   end;
  end;
end;

procedure TMainFrm.FormCreate(Sender: TObject);
var sli:TStringList; st:integer; i,j,k:integer;
 cat:TCCat; s,imgext:string; plst: TPropsList;
 xml:TjanXMLParser2; node1, node2, node3, IncNode: TjanXMLNode2; nl:TjanXMLNodeList2;
begin
 ShowProgress('Przygotowywanie programu do działania...');
 QuotedParams := TStringList.Create;
// ItemSelector.LoadFromFile('.\data\list.txt');
 //tf := TIn.Create('.\example.ini');
 //TotalObjects := tf.ReadInteger('Info', 'ObjectsCount', 0);
 ObjectProperties.Cells[0,0] := 'Nazwa';
 ObjectProperties.Cells[1,0] := 'Wartość';
 //sl := TStringList.Create;
 sli := TStringList.Create;
 //slp := TStringList.Create;


 ShowProgress('Czytanie głównego pliku XML danych obiektów...');
 xml := TjanXMLParser2.create;
 xml.LoadXML('.\data\items.xml');
 //f := TIniFile.Create('.\data\items\basic.ini');
 //fp := TIniFile.Create('.\data\items\props.ini');
 //fi := TIniFile.Create('.\data\items\propinc.ini');
 //f.ReadSection('lnames', sl);
 
 node1 := xml.getChildByName('info');
 node2 := node1.getChildByName('PhotoFormat');
 imgext := node2.attribute['extension'];
 ShowProgress('Ładowanie specjalnych obrazków...');
 PicNothing := TPicture.Create;
 PicNothing.LoadFromFile('.\data\special_img\nothing'+imgext);
 PicNoPhoto := TPicture.Create;
 PicNoPhoto.LoadFromFile('.\data\special_img\noimg'+imgext);

 ShowProgress('Ładowanie danych kategorii...');
 IncNode := xml.getChildByName('Props_inc');
 node1 := xml.getChildByName('Categories');
 nl := node1.nodes;
 for i := 0 to nl.Count-1 do begin
  //ShowProgress('Ładowanie danych kategorii: '+sl[i], false);
  cat := TCCat.Create;
  AllCats.Add(cat);
  node2 := TjanXMLNode2(nl.Items[i]);
  cat.CatName := node2.name;
  cat.LongName := node2.attribute['title'];
  cat.Radius := StrToDir(node2.attribute['radius']);

  s := '.\data\items_img\'+cat.CatName+imgext;
  if FileExists(s) then begin
   cat.Photo := TPicture.Create;
   cat.Photo.LoadFromFile(s);
  end else cat.Photo := PicNoPhoto;
  s := node2.attribute['kind'];
  if s='bot' then cat.ItemType := itBot else
  if s='build' then cat.ItemType := itBuilding else
  if s='alien' then cat.ItemType := itAlien else
  if s='portable' then cat.ItemType := itPortable else
  if s='decor' then cat.ItemType := itDecor else cat.ItemType := itOther;
 end;

 //fi.ReadSections(sl);
 node1 := xml.getChildByName('Props_inc');
 ShowProgress('Ładowanie właściwości załączanych');
 for i := 0 to node1.nodes.Count-1 do begin
  node2 := TjanXMLNode2(node1.nodes[i]);
  ShowProgress('Ładowanie właściwości załączanej: '+node2.Name, false);
  if node2.getChildByName('ApplyFor')<>nil then begin
   sli.Clear;
   Explode(sli,',', node2.getChildByName('ApplyFor').attribute['cats']);

   for j := 0 to sli.Count-1 do begin
    cat := AllCats.Cats[sli[j]];
    ReadPropsFromNode(node2, cat.Props, cat.OptProps, IncNode);
   end;
  end;
 end;

 ShowProgress('Ładowanie załączonych i podstawowych właściwości...');
 node1 := xml.getChildByName('Categories');
 for i := 0 to AllCats.Count-1 do begin
  cat := TCCat(AllCats.Items[i]);
  ShowProgress('Ładowanie właściwości kategorii: '+cat.CatName, false);
  ReadPropsFromNode(node1.getChildByName(cat.CatName), cat.Props, cat.OptProps, IncNode);
 end;
 ShowProgress('Tworzenie menu kategorii...');

 node1 := xml.getChildByName('Categories_menu');
 ReadMenuNodesFromXml(nil,
  ItemSelector.Items, node1);

 ShowProgress('Kończenie ładowania...');
 //sl.Free;
 sli.Free;
 //slp.Free;
 //f.Free;
 //fp.Free;
 //fi.Free;

 AllObjects := TList.Create;
 ShownRect.Left := -400;
 ShownRect.Top := -400;
 ShownRect.Right := 400;
 ShownRect.Bottom := 400;
 relief := TBitmap.Create;
 relief.LoadFromFile('.\data\defmap.bmp');
 ShowProgress('Ukończono ładowanie danych.');
 //Sleep(1000);
 HideProgress;
end;

procedure TMainFrm.FormClose(Sender: TObject; var Action: TCloseAction);
var i:integer;
begin
 //tf.Free;
 for i := AllObjects.Count-1 downto 0 do TCObject(AllObjects.Items[i]).Free;

 AllObjects.Free;
 relief.Free;
// cind^ := 1;
 Application.Terminate;
end;

procedure TMainFrm.Ustaw1Click(Sender: TObject);
var s:string;
begin
 s := FloatToStrF(PxPerMetre, ffGeneral,5,2);
 try
  if InputQuery('Ustawianie powiększenia', 'Wprowadź powiększenie (px/m):', s)
   then PxPerMetre := StrToFloat(s);
 except
  MessageBox(0, 'Błędna wartość powiększenia.', 'Błąd!', MB_ICONERROR);
 end;
 //UpdateSize;
 UpdateMap;
end;

procedure TMainFrm.Odswie1Click(Sender: TObject);
begin
 UpdateMap;
end;

procedure TMainFrm.Zwiksz1Click(Sender: TObject);
begin
 PxPerMetre := PxPerMetre*1.5;
 UpdateMap;
end;

procedure TMainFrm.Zmniejsz1Click(Sender: TObject);
begin
 PxPerMetre := PxPerMetre/1.5;
 UpdateMap;
end;

procedure TMainFrm.FormKeyPress(Sender: TObject; var Key: Char);
begin    {
 if Ord(Key)=VK_LEFT then Dec(VisibleZoneX);
 if Ord(Key)=VK_RIGHT then Inc(VisibleZoneX);
 if Ord(Key)=VK_Up then Dec(VisibleZoneY);
 if Ord(Key)=Vk_Down then Inc(VisibleZoneY);}
end;

procedure TMainFrm.Eksportujdoplikugry1Click(Sender: TObject);
var i,j:integer; sl:TStringList; s:string; obj:TCObject; prop:TObjProp;
begin
 OutputFrm.CodeMemo.Lines.Clear;
 for i := 0 to AllObjects.Count-1 do begin
  s := 'CreateObject ';
  //sl := TStringList.Create;
  //tf.ReadSection('obj_'+IntToStr(i), sl);
  obj := TCObject(AllObjects.Items[i]);
  if not Assigned(obj.category) then continue;
  s := s+'pos='+ObjPosToStr(obj.position)+' dir='+
   StringReplace(FloatToStrF(obj.orientation,ffFixed,5,2),',','.',[])+' type='+
   obj.category.CatName+' ';
  for j := 0 to obj.Props.Count-1 do begin
   prop := TObjProp(obj.Props.Items[j]^);
   if QuotedParams.IndexOf(prop.Name)<0 then
    s := s+prop.Name+'='+prop.Value+' ' else
    s := s+prop.Name+'="'+prop.Value+'" ';
  end;
  OutputFrm.CodeMemo.Lines.Add(s);
 end;
 OutputFrm.Show;
end;

procedure TMainFrm.Nowawaciwo1Click(Sender: TObject);
begin
 ObjectProperties.RowCount := ObjectProperties.RowCount+1;
end;

procedure TMainFrm.Usuzaznwaciwo1Click(Sender: TObject);
var i:byte;
begin
 for i := ObjectProperties.Row to ObjectProperties.RowCount-1 do
  ObjectProperties.Rows[i] := ObjectProperties.Rows[i+1];
 ObjectProperties.RowCount := ObjectProperties.RowCount-1;
end;

procedure TMainFrm.PropsConfirmBtnClick(Sender: TObject);
var i:integer;
begin
 SelectedObject.category := AllCats.Cats[CatEdt.Text];
 SelectedObject.position := StrToObjPos(PosEdt.Text);
 SelectedObject.orientation := StrToFloat(DirEdt.Text);
 for i := SelectedObject.Props.Count-1 downto 0 do begin
  FreeMem(SelectedObject.Props.Items[i]);
  SelectedObject.Props.Delete(i);
 end;
 for i := 1 to ObjectProperties.RowCount-1 do
  WriteProperty(ObjectProperties.Cells[0,i],
  ObjectProperties.Cells[1,i]);
 RefreshObjectInfo;
end;

procedure TMainFrm.OpenFileBtnClick(Sender: TObject);
var f:TIniFile; sl,pl:TStringList; obj:TCObject; i:cardinal; j:word;
begin
 if FileOpen.Execute then begin
  f := TIniFile.Create(FileOpen.FileName);
  sl := TStringList.Create;
  pl := TStringList.Create;
  f.ReadSections(sl);
  ReliefFile := f.ReadString('info','relief','.\data\defmap.bmp');
  relief.LoadFromFile(ReliefFile);
  for i := 0 to sl.Count-1 do begin
   if sl[i]='info' then continue;
   obj := TCObject.Create;
   AllObjects.Add(obj);
   obj.category := AllCats.Cats[f.ReadString(sl[i], 'type', 'null')];
   obj.position := StrToObjPos(f.ReadString(sl[i], 'pos', '0;0'));
   obj.orientation := f.ReadFloat(sl[i], 'dir', 0);
   obj.Props := TPropsList.Create;
   f.ReadSection(sl[i], pl);
   for j := 0 to pl.Count-1 do begin
    if (pl[j]='type') or (pl[j]='pos') or (pl[j]='dir') then continue;
    obj.Props.Values[pl[j]] := f.ReadString(sl[i], pl[j], '');
   end;
  end;
  f.Free;
  sl.Free;
  pl.Free;
  ChDir(ExtractFileDir(Application.ExeName));
 end;
end;

procedure TMainFrm.SaveFileBtnClick(Sender: TObject);
var f:TIniFile; i:cardinal; sn:string; obj:TCObject; j:word; prop:TObjProp;
begin
 if FileSave.Execute then begin
  f := TIniFile.Create(FileSave.FileName);
  for i := 0 to AllObjects.Count-1 do begin
   obj := TCObject(AllObjects.Items[i]);
   sn := 'obj'+IntToStr(i);
   f.WriteString(sn, 'pos', ObjPosToStr(obj.position));
   f.WriteFloat(sn, 'dir', obj.orientation);
   f.WriteString(sn, 'type', obj.category.CatName);
   for j := 0 to obj.Props.Count-1 do begin
    prop := TObjProp(obj.Props.Items[j]^);
    f.WriteString(sn, prop.Name, prop.Value);
   end;
  end;
 end;
 f.Free;
 ChDir(ExtractFileDir(Application.ExeName));
end;

procedure TMainFrm.DrawBoxMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
 if ZoneSelRd.Down and (Button=mbLeft) then begin
  ZoneStart := Point(x,y);
  ZoneShape.Visible := true;
  ZoneShape.Left := x+DrawBox.Left;
  ZoneShape.Top := y+DrawBox.Top;
 end else if Button=mbMiddle then begin
  movingview := true;
  movingstart := Point(x,y);
 end;
end;

procedure TMainFrm.DrawBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
 if ZoneSelRd.Down then begin
  ZoneShape.Width := x-ZoneStart.X;
  ZoneShape.Height := y-ZoneStart.Y;
 end;
 CursorMapPos := PointToObjPos(Point(x,y));
 if movingview and ((movingstart.x<>x)or(movingstart.y<>Y)) then begin
  with ShownRect do begin
   Left := Left-PxToMetres(X-movingstart.X);
   Right := Right-PxToMetres(X-movingstart.X);
   Top := Top-PxToMetres(Y-movingstart.Y);
   Bottom := Bottom-PxToMetres(Y-movingstart.Y);
   movingstart := point(x,y);
  end;
  UpdateMap;
 end;
end;

procedure TMainFrm.FullViewBtnClick(Sender: TObject);
begin
 ShownRect.Left := -400;
 ShownRect.Top := -400;
 ShownRect.Right := 400;
 ShownRect.Bottom := 400;
 PxPerMetre := 0.7;
 UpdateMap;
end;

procedure TMainFrm.ZoomTrackChange(Sender: TObject);
begin
 PxPerMetre := ZoomTrack.Position/100;
 UpdateMap;
end;

procedure TMainFrm.OptPropsLstDblClick(Sender: TObject);
var op:TObjProp; i:integer;
begin
 ObjectProperties.RowCount := ObjectProperties.RowCount+1;
 op := TObjProp(SelectedObject.category.OptProps.Items[OptPropsLst.ItemIndex]^);
 ObjectProperties.Cells[0, ObjectProperties.RowCount-1] := op.Name;
 ObjectProperties.Cells[1, ObjectProperties.RowCount-1] := op.Value;
end;

procedure TMainFrm.Zaadujrelief1Click(Sender: TObject);
begin
 if ReliefOpen.Execute then begin
  ReliefFile := ReliefOpen.FileName;
  relief.LoadFromFile(ReliefFile);
 end;
 Chdir(ExtractFileDir(Application.Exename));
end;

procedure TMainFrm.Importujzplikugry1Click(Sender: TObject);
var osl, sl:TStringList; i,j:integer; ch:char; s:string;
 quoted, ispos, isname, qend:bool; name, value:string; obj:TCObject;
 numds:Smallint; wasspace: bool;
begin
 if not SceneFileOpen.Execute then exit;
 ShowProgress('Przygotowywanie importu pliku: '+SceneFileOpen.FileName);
 numds := 0;
 wasspace := true;
 osl := TStringList.Create;
 sl := TStringList.Create;
 osl.LoadFromFile(SceneFileOpen.FileName);
 ChDir(ExtractFileDir(Application.ExeName));
 ShowProgress('Segregowanie linii...');
 for i := 0 to osl.Count-1 do if Pos('CreateObject', osl[i])=1 then
  sl.Add(RightStr(osl[i], Length(osl[i])-13));
 osl.Free;
 ShowProgress('Przetwarzanie linii...');
 for i := 0 to sl.Count-1 do begin
  ShowProgress('Przetwarzanie: `'+sl[i]+'`', false);
  obj := TCObject.Create;
  obj.Props := TPropsList.Create;
  AllObjects.Add(obj);
  for j := 1 to Length(sl[i]) do begin
   ch := sl[i][j];
   qend := false;
   if quoted then begin
    if (ch<>'"') then s := s+ch else begin
     quoted := false;
     qend := true;
    end;
   end;
   //if (numds>0) and (ch=';') then numds := numds-1;
   if (not quoted) and ((not qend) or (j=Length(sl[i]))) then begin
    if
     (name<>'')
     and
     (
      (
       ((ch>='a') and (ch<='z'))
       and
       wasspace
      )
      or
      (j=Length(sl[i]))
     )
    then begin
     if (ch<>' ') and (not qend) and (j=Length(sl[i])) then s := s+ch;
     wasspace := false;
     value := s;
     s := '';
     if not (j=Length(sl[i])) then s := ch;
     isname := true;
     if name='pos' then obj.position := StrToObjPos(value) else
     if name='dir' then obj.orientation := StrToDir(value) else
     if name='type' then obj.category := AllCats.Cats[value] else begin
      //if Pos('info', name)=1 then ShowMessage(name+' = '+value);
      obj.Props.Values[name] := value;
     end;
     continue;
    end;
    case ch of
     '"': quoted := not quoted;
     '=': if not quoted then begin
      isname := false;
      name := s;
      if name='pos' then numds := 1;

      s := '';
     end;
     else begin
      if quoted or (ch<>' ') then s := s+ch;
      if (ch=' ') and (not quoted) then wasspace := true;
     end;
    end;
   end;
  end;
 end;
 ShowProgress('Zakończono przetwarzanie.');
 UpdateMap;
end;

procedure TMainFrm.DelAllBtnClick(Sender: TObject);
var i:integer;
begin
 if MessageBox(Handle, 'Czy na pewno usunąć wszystkie obiekty?',
   'Potwierdź operację', MB_OKCANCEL+MB_ICONWARNING)<>idOk then exit;
 for i := AllObjects.Count-1 downto 0 do begin
  TCObject(AllObjects.Items[i]).Free;
  AllObjects.Delete(i);
 end;
 UpdateMap;
end;

procedure TMainFrm.Standardowe1Click(Sender: TObject);
begin
 PxPerMetre := 1;
 UpdateMap;
end;

procedure TMainFrm.Pokalog1Click(Sender: TObject);
begin
 LoadingFrm.Show;
end;

procedure TMainFrm.Zakocz1Click(Sender: TObject);
begin
 Close;
end;

procedure TMainFrm.Oprogramie1Click(Sender: TObject);
begin
 AboutFrm.Image1.Picture.LoadFromFile('.\data\developer_logo.jpg');
 AboutFrm.Show;
end;

procedure TMainFrm.Restartprogramu1Click(Sender: TObject);
begin
 ShellExecute(0, 'open', PChar(Application.ExeName), nil, nil, SW_SHOWNORMAL);
 Close;
end;

procedure TMainFrm.FormMouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var cx, cy, sx, sy, px, py: single; np: TObjPos; psr: TFloatRect;
begin
// cx := (ShownRect.Left+ShownRect.Right)/2;
// cy := (ShownRect.Top+ShownRect.Bottom)/2;

 sx := PxToMetres(DrawBox.Width);
 sy := PxToMetres(DrawBox.Height);

 np := CursorMapPos;
 px := abs(np.x-ShownRect.Left)/sx;
 py := abs(-np.y-ShownRect.Top)/sy;

 PxPerMetre := PxPerMetre-WheelDelta/500;
 if PxPerMetre<0.5 then PxPerMetre := 0.5;
 
 sx := PxToMetres(DrawBox.Width);
 sy := PxToMetres(DrawBox.Height);

 psr := ShownRect;
 with ShownRect do begin
  Left := np.x-sx*px;
  Top := -np.y-sy*py;
  Right := np.x+sx*(1-px);
  Bottom := -np.y+sy*(1-py);
  {if Left<-400 then begin
   Left := -400;
   Right := Right+(Left-psr.Left);
  end;
  if Right>400 then begin
   Right := 400;
   Left := Left-(psr.Right-Right);
  end;
  if Bottom<-400 then begin
   Bottom := -400;
   Top := Top+(Bottom-psr.Bottom);
  end;
  if Top>400 then begin
   Top := 400;
   Bottom := Bottom-(psr.Top-Top);
  end; }
 end;
 UpdateMap;
end;

procedure TMainFrm.ScrollerResize(Sender: TObject);
begin
 if Scroller.ClientWidth<Scroller.ClientHeight then
  DrawBox.Width := Scroller.ClientWidth else
  DrawBox.Width := Scroller.ClientHeight;
 DrawBox.Height := DrawBox.Width;       
 UpdateMap;
end;

procedure TMainFrm.FormResize(Sender: TObject);
var h:boolean;
begin
 ScrollerResize(Sender);
 FormMouseWheel(Sender, [], 0, Point(0,0), h);
end;

end.

