DEADSOFTWARE

copy map resources before test map
[d2df-editor.git] / src / editor / f_main.pas
index 82bd18dec1de2ee47bb2044fb12b491f2c8e6527..5a246b11eaa79571b8954d2eabd6a3ac94ab501b 100644 (file)
@@ -8,7 +8,7 @@ uses
   LCLIntf, LCLType, SysUtils, Variants, Classes, Graphics,
   Controls, Forms, Dialogs, StdCtrls, Buttons,
   ComCtrls, ValEdit, Types, Menus, ExtCtrls,
   LCLIntf, LCLType, SysUtils, Variants, Classes, Graphics,
   Controls, Forms, Dialogs, StdCtrls, Buttons,
   ComCtrls, ValEdit, Types, Menus, ExtCtrls,
-  CheckLst, Grids, OpenGLContext, utils, UTF8Process;
+  CheckLst, Grids, OpenGLContext, Utils, UTF8Process;
 
 type
 
 
 type
 
@@ -266,6 +266,8 @@ type
   public
     procedure RefreshRecentMenu();
     procedure OpenMapFile(FileName: String);
   public
     procedure RefreshRecentMenu();
     procedure OpenMapFile(FileName: String);
+    function RenderMousePos(): TPoint;
+    procedure RecountSelectedObjects();
   end;
 
 const
   end;
 
 const
@@ -290,8 +292,8 @@ var
 
   DotColor: TColor;
   DotEnable: Boolean;
 
   DotColor: TColor;
   DotEnable: Boolean;
-  DotStep: Byte;
-  DotStepOne, DotStepTwo: Byte;
+  DotStep: Word;
+  DotStepOne, DotStepTwo: Word;
   DotSize: Byte;
   DrawTexturePanel: Boolean;
   DrawPanelSize: Boolean;
   DotSize: Byte;
   DrawTexturePanel: Boolean;
   DrawPanelSize: Boolean;
@@ -311,11 +313,13 @@ var
   TestOptionsAllowExit: Boolean;
   TestOptionsWeaponStay: Boolean;
   TestOptionsMonstersDM: Boolean;
   TestOptionsAllowExit: Boolean;
   TestOptionsWeaponStay: Boolean;
   TestOptionsMonstersDM: Boolean;
-  TestD2dExe: String;
+  TestD2dExe, TestD2DArgs: String;
   TestMapOnce: Boolean;
 
   LayerEnabled: Array [LAYER_BACK..LAYER_TRIGGERS] of Boolean =
     (True, True, True, True, True, True, True, True, True);
   TestMapOnce: Boolean;
 
   LayerEnabled: Array [LAYER_BACK..LAYER_TRIGGERS] of Boolean =
     (True, True, True, True, True, True, True, True, True);
+  ContourEnabled: Array [LAYER_BACK..LAYER_TRIGGERS] of Boolean =
+    (False, False, False, False, False, False, False, False, False);
   PreviewMode: Byte = 0;
   gLanguage: String;
 
   PreviewMode: Byte = 0;
   gLanguage: String;
 
@@ -333,11 +337,11 @@ uses
   f_options, e_graphics, e_log, GL, Math,
   f_mapoptions, g_basic, f_about, f_mapoptimization,
   f_mapcheck, f_addresource_texture, g_textures,
   f_options, e_graphics, e_log, GL, Math,
   f_mapoptions, g_basic, f_about, f_mapoptimization,
   f_mapcheck, f_addresource_texture, g_textures,
-  f_activationtype, f_keys,
-  MAPREADER, f_selectmap, f_savemap, WADEDITOR, WADSTRUCT, MAPDEF,
+  f_activationtype, f_keys, wadreader, fileutil,
+  MAPREADER, f_selectmap, f_savemap, WADEDITOR, MAPDEF,
   g_map, f_saveminimap, f_addresource, CONFIG, f_packmap,
   f_addresource_sound, f_maptest, f_choosetype,
   g_map, f_saveminimap, f_addresource, CONFIG, f_packmap,
   f_addresource_sound, f_maptest, f_choosetype,
-  g_language, f_selectlang, ClipBrd;
+  g_language, f_selectlang, ClipBrd, g_resources;
 
 const
   UNDO_DELETE_PANEL   = 1;
 
 const
   UNDO_DELETE_PANEL   = 1;
@@ -438,9 +442,10 @@ var
   LastMovePoint: Types.TPoint;
   MouseLDown: Boolean;
   MouseRDown: Boolean;
   LastMovePoint: Types.TPoint;
   MouseLDown: Boolean;
   MouseRDown: Boolean;
+  MouseMDown: Boolean;
   MouseLDownPos: Types.TPoint;
   MouseRDownPos: Types.TPoint;
   MouseLDownPos: Types.TPoint;
   MouseRDownPos: Types.TPoint;
-  WASDOffset: TPoint;
+  MouseMDownPos: Types.TPoint;
 
   SelectFlag: Byte = SELECTFLAG_NONE;
   MouseAction: Byte = MOUSEACTION_NONE;
 
   SelectFlag: Byte = SELECTFLAG_NONE;
   MouseAction: Byte = MOUSEACTION_NONE;
@@ -691,10 +696,10 @@ begin
   begin
     ScaleSz := 16 div Scale;
   // Размер видимой части карты:
   begin
     ScaleSz := 16 div Scale;
   // Размер видимой части карты:
-    rx := min(Normalize16(Width), Normalize16(gMapInfo.Width)) div 2;
-    ry := min(Normalize16(Height), Normalize16(gMapInfo.Height)) div 2;
+    rx := Min(Normalize16(Width), Normalize16(gMapInfo.Width)) div 2;
+    ry := Min(Normalize16(Height), Normalize16(gMapInfo.Height)) div 2;
   // Место клика на мини-карте:
   // Место клика на мини-карте:
-    MapOffset.X := X - (Width-max(gMapInfo.Width div ScaleSz, 1)-1);
+    MapOffset.X := X - (Width - Max(gMapInfo.Width div ScaleSz, 1) - 1);
     MapOffset.Y := Y - 1;
   // Это же место на "большой" карте:
     MapOffset.X := MapOffset.X * ScaleSz;
     MapOffset.Y := Y - 1;
   // Это же место на "большой" карте:
     MapOffset.X := MapOffset.X * ScaleSz;
@@ -703,17 +708,11 @@ begin
     MapOffset.X := MapOffset.X - rx;
     MapOffset.Y := MapOffset.Y - ry;
   // Выход за границы:
     MapOffset.X := MapOffset.X - rx;
     MapOffset.Y := MapOffset.Y - ry;
   // Выход за границы:
-    if MapOffset.X < 0 then
-      MapOffset.X := 0;
-    if MapOffset.Y < 0 then
-      MapOffset.Y := 0;
-    if MapOffset.X > MainForm.sbHorizontal.Max then
-      MapOffset.X := MainForm.sbHorizontal.Max;
-    if MapOffset.Y > MainForm.sbVertical.Max then
-      MapOffset.Y := MainForm.sbVertical.Max;
+    MapOffset.X := EnsureRange(MapOffset.X, MainForm.sbHorizontal.Min, MainForm.sbHorizontal.Max);
+    MapOffset.Y := EnsureRange(MapOffset.Y, MainForm.sbVertical.Min, MainForm.sbVertical.Max);
   // Кратно 16:
   // Кратно 16:
-    MapOffset.X := Normalize16(MapOffset.X);
-    MapOffset.Y := Normalize16(MapOffset.Y);
+  //  MapOffset.X := Normalize16(MapOffset.X);
+  //  MapOffset.Y := Normalize16(MapOffset.Y);
   end;
 
   MainForm.sbHorizontal.Position := MapOffset.X;
   end;
 
   MainForm.sbHorizontal.Position := MapOffset.X;
@@ -738,6 +737,7 @@ var
   str: String;
 begin
   MainForm.vleObjectProperty.Strings.Clear();
   str: String;
 begin
   MainForm.vleObjectProperty.Strings.Clear();
+  MainForm.RecountSelectedObjects();
 
 // Отображаем свойства если выделен только один объект:
   if SelectedObjectCount() <> 1 then
 
 // Отображаем свойства если выделен только один объект:
   if SelectedObjectCount() <> 1 then
@@ -1406,6 +1406,20 @@ begin
                   EditStyle := esSimple;
                   MaxLength := 5;
                 end;
                   EditStyle := esSimple;
                   MaxLength := 5;
                 end;
+                case Data.DamageKind of
+                  3: str := _lc[I_PROP_TR_DAMAGE_KIND_3];
+                  4: str := _lc[I_PROP_TR_DAMAGE_KIND_4];
+                  5: str := _lc[I_PROP_TR_DAMAGE_KIND_5];
+                  6: str := _lc[I_PROP_TR_DAMAGE_KIND_6];
+                  7: str := _lc[I_PROP_TR_DAMAGE_KIND_7];
+                  8: str := _lc[I_PROP_TR_DAMAGE_KIND_8];
+                  else str := _lc[I_PROP_TR_DAMAGE_KIND_0];
+                end;
+                with ItemProps[InsertRow(_lc[I_PROP_TR_DAMAGE_KIND], str, True)] do
+                begin
+                  EditStyle := esPickList;
+                  ReadOnly := True;
+                end;
               end;
 
             TRIGGER_HEALTH:
               end;
 
             TRIGGER_HEALTH:
@@ -1479,7 +1493,7 @@ begin
                   3: str := _lc[I_PROP_TR_SHOT_AIM_3];
                   else str := _lc[I_PROP_TR_SHOT_AIM_0];
                 end;
                   3: str := _lc[I_PROP_TR_SHOT_AIM_3];
                   else str := _lc[I_PROP_TR_SHOT_AIM_0];
                 end;
-                with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_AIM], str, True)-1] do
+                with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_AIM], str, True)] do
                 begin
                   EditStyle := esPickList;
                   ReadOnly := True;
                 begin
                   EditStyle := esPickList;
                   ReadOnly := True;
@@ -1773,6 +1787,7 @@ begin
   RemoveSelectFromObjects();
 
   MainForm.miUndo.Enabled := UndoBuffer <> nil;
   RemoveSelectFromObjects();
 
   MainForm.miUndo.Enabled := UndoBuffer <> nil;
+  MainForm.RecountSelectedObjects();
 end;
 
 procedure Undo_Add(ObjectType: Byte; ID: DWORD; Group: Boolean = False);
 end;
 
 procedure Undo_Add(ObjectType: Byte; ID: DWORD; Group: Boolean = False);
@@ -2564,45 +2579,27 @@ end;
 
 procedure TMainForm.aRecentFileExecute(Sender: TObject);
 var
 
 procedure TMainForm.aRecentFileExecute(Sender: TObject);
 var
-  n, pw: Integer;
-  s, fn: String;
-  b: Boolean;
+  n: Integer;
+  fn, s: String;
 begin
   s := LowerCase((Sender as TMenuItem).Caption);
   Delete(s, Pos('&', s), 1);
   s := Trim(Copy(s, 1, 2));
   n := StrToIntDef(s, 0) - 1;
 begin
   s := LowerCase((Sender as TMenuItem).Caption);
   Delete(s, Pos('&', s), 1);
   s := Trim(Copy(s, 1, 2));
   n := StrToIntDef(s, 0) - 1;
-
-  if (n < 0) or (n >= RecentFiles.Count) then
-    Exit;
-
-  s := RecentFiles[n];
-  pw := Pos('.wad:\', LowerCase(s));
-  b := False;
-  
-  if pw > 0 then
-    begin // Map name included
-      fn := Copy(s, 1, pw + 3);
-      Delete(s, 1, pw + 5);
-      if (FileExists(fn)) then
-      begin
-        OpenMap(fn, s);
-        b := True;
-      end;
+  if (n >= 0) and (n <= RecentFiles.Count) then
+  begin
+    fn := g_ExtractWadName(RecentFiles[n]);
+    if FileExists(fn) then
+    begin
+      s := g_ExtractFilePathName(RecentFiles[n]);
+      OpenMap(fn, s)
     end
     end
-  else // Only wad name
-    if (FileExists(s)) then
+    else if MessageBox(0, PChar(_lc[I_MSG_DEL_RECENT_PROMT]), PChar(_lc[I_MSG_DEL_RECENT]), MB_ICONQUESTION or MB_YESNO) = idYes then
     begin
     begin
-      OpenMap(s, '');
-      b := True;
-    end;
-
-  if (not b) and (MessageBox(0, PChar(_lc[I_MSG_DEL_RECENT_PROMT]),
-    PChar(_lc[I_MSG_DEL_RECENT]), MB_ICONQUESTION or MB_YESNO) = idYes) then
-  begin
-    RecentFiles.Delete(n);
-    RefreshRecentMenu();
-  end;
+      RecentFiles.Delete(n);
+      RefreshRecentMenu();
+    end
+  end
 end;
 
 procedure TMainForm.aEditorOptionsExecute(Sender: TObject);
 end;
 
 procedure TMainForm.aEditorOptionsExecute(Sender: TObject);
@@ -2615,23 +2612,15 @@ var
   cwdt, chgt: Byte;
   spc: ShortInt;
   ID: DWORD;
   cwdt, chgt: Byte;
   spc: ShortInt;
   ID: DWORD;
-  wad: TWADEditor_1;
   cfgdata: Pointer;
   cfglen: Integer;
   config: TConfig;
 begin
   cfgdata: Pointer;
   cfglen: Integer;
   config: TConfig;
 begin
-  cfgdata := nil;
-  cfglen := 0;
   ID := 0;
   ID := 0;
-
-  wad := TWADEditor_1.Create;
-  if wad.ReadFile(EditorDir+'data/Game.wad') then
-    wad.GetResource('FONTS', cfgres, cfgdata, cfglen);
-  wad.Free();
-
-  if cfglen <> 0 then
+  g_ReadResource(EditorDir + 'data/game.wad', 'FONTS', cfgres, cfgdata, cfglen);
+  if cfgdata <> nil then
   begin
   begin
-    if not g_CreateTextureWAD('FONT_STD', EditorDir+'data/Game.wad:FONTS\'+texture) then
+    if not g_CreateTextureWAD('FONT_STD', EditorDir + 'data/game.wad:FONTS\' + texture) then
       e_WriteLog('ERROR ERROR ERROR', MSG_WARNING);
 
     config := TConfig.CreateMem(cfgdata, cfglen);
       e_WriteLog('ERROR ERROR ERROR', MSG_WARNING);
 
     config := TConfig.CreateMem(cfgdata, cfglen);
@@ -2640,14 +2629,15 @@ begin
     spc := Min(Max(config.ReadInt('FontMap', 'Kerning', 0), -128), 127);
 
     if g_GetTexture('FONT_STD', ID) then
     spc := Min(Max(config.ReadInt('FontMap', 'Kerning', 0), -128), 127);
 
     if g_GetTexture('FONT_STD', ID) then
-      e_TextureFontBuild(ID, FontID, cwdt, chgt, spc-2);
+      e_TextureFontBuild(ID, FontID, cwdt, chgt, spc - 2);
 
     config.Free();
 
     config.Free();
+    FreeMem(cfgdata)
   end
   else
   end
   else
-    e_WriteLog('Could not load FONT_STD', MSG_WARNING);
-
-  if cfglen <> 0 then FreeMem(cfgdata);
+  begin
+    e_WriteLog('Could not load FONT_STD', MSG_WARNING)
+  end
 end;
 
 procedure TMainForm.FormCreate(Sender: TObject);
 end;
 
 procedure TMainForm.FormCreate(Sender: TObject);
@@ -2725,6 +2715,8 @@ begin
   gAlphaTriggerArea := config.ReadInt('Editor', 'TriggerAlpha', ALPHA_AREA);
   if gAlphaTriggerArea = 255 then
     gAlphaTriggerArea := ALPHA_AREA;
   gAlphaTriggerArea := config.ReadInt('Editor', 'TriggerAlpha', ALPHA_AREA);
   if gAlphaTriggerArea = 255 then
     gAlphaTriggerArea := ALPHA_AREA;
+  gAlphaMonsterRect := config.ReadInt('Editor', 'MonsterRectAlpha', 0);
+  gAlphaAreaRect := config.ReadInt('Editor', 'AreaRectAlpha', 0);
   if config.ReadInt('Editor', 'Scale', 0) = 1 then
     Scale := 2
   else
   if config.ReadInt('Editor', 'Scale', 0) = 1 then
     Scale := 2
   else
@@ -2739,6 +2731,9 @@ begin
   s := config.ReadStr('Editor', 'Language', '');
   gLanguage := s;
 
   s := config.ReadStr('Editor', 'Language', '');
   gLanguage := s;
 
+  Compress := config.ReadBool('Editor', 'Compress', True);
+  Backup := config.ReadBool('Editor', 'Backup', True);
+
   RecentCount := config.ReadInt('Editor', 'RecentCount', 5);
   if RecentCount > 10 then
     RecentCount := 10;
   RecentCount := config.ReadInt('Editor', 'RecentCount', 5);
   if RecentCount > 10 then
     RecentCount := 10;
@@ -2836,12 +2831,22 @@ begin
     else
       a := 0;
 
     else
       a := 0;
 
-    for x := 0 to (RenderPanel.Width div DotStep) do
-      for y := 0 to (RenderPanel.Height div DotStep) do
-        e_DrawPoint(DotSize, x*DotStep + a, y*DotStep + a,
+    x := MapOffset.X mod DotStep;
+    y := MapOffset.Y mod DotStep;
+
+    while x < RenderPanel.Width do
+    begin
+      while y < RenderPanel.Height do
+      begin
+        e_DrawPoint(DotSize, x + a, y + a,
                     GetRValue(DotColor),
                     GetGValue(DotColor),
                     GetBValue(DotColor));
                     GetRValue(DotColor),
                     GetGValue(DotColor),
                     GetBValue(DotColor));
+        y += DotStep;
+      end;
+      x += DotStep;
+      y := MapOffset.Y mod DotStep;
+    end;
   end;
 
 // Превью текстуры:
   end;
 
 // Превью текстуры:
@@ -2947,8 +2952,9 @@ begin
       g_GetTexture('NOTEXTURE', ID);
     g_GetTextureSizeByID(ID, Width, Height);
     with DrawRect^ do
       g_GetTexture('NOTEXTURE', ID);
     g_GetTextureSizeByID(ID, Width, Height);
     with DrawRect^ do
-      e_DrawFill(ID, Min(Left, Right), Min(Top, Bottom), Abs(Right-Left) div Width,
-                 Abs(Bottom-Top) div Height, 0, True, False);
+      if (Abs(Right-Left) >= Width) and (Abs(Bottom-Top) >= Height) then
+        e_DrawFill(ID, Min(Left, Right), Min(Top, Bottom), Abs(Right-Left) div Width,
+                   Abs(Bottom-Top) div Height, 64, True, False);
   end;
 
 // Прямоугольник выделения:
   end;
 
 // Прямоугольник выделения:
@@ -2957,7 +2963,8 @@ begin
       e_DrawQuad(Left, Top, Right-1, Bottom-1, 255, 255, 255);
 
 // Чертим мышью панель/триггер или меняем мышью их размер:
       e_DrawQuad(Left, Top, Right-1, Bottom-1, 255, 255, 255);
 
 // Чертим мышью панель/триггер или меняем мышью их размер:
-  if (MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER, MOUSEACTION_RESIZE]) and
+  if (((MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER]) and
+     not(ssCtrl in GetKeyShiftState())) or (MouseAction = MOUSEACTION_RESIZE)) and
      (DrawPanelSize) then
   begin
     e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+88, MousePos.Y+33, 192, 192, 192, 127);
      (DrawPanelSize) then
   begin
     e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+88, MousePos.Y+33, 192, 192, 192, 127);
@@ -2967,7 +2974,7 @@ begin
       begin // Чертим новый
         PrintBlack(MousePos.X+2, MousePos.Y+2, Format(_glc[I_HINT_WIDTH],
                           [Abs(MousePos.X-MouseLDownPos.X)]), gEditorFont);
       begin // Чертим новый
         PrintBlack(MousePos.X+2, MousePos.Y+2, Format(_glc[I_HINT_WIDTH],
                           [Abs(MousePos.X-MouseLDownPos.X)]), gEditorFont);
-        PrintBlack(MousePos.X+2, MousePos.Y+14, Format(_glc[I_HINT_HEIGHT],
+        PrintBlack(MousePos.X+2, MousePos.Y+16, Format(_glc[I_HINT_HEIGHT],
                           [Abs(MousePos.Y-MouseLDownPos.Y)]), gEditorFont);
       end
     else // Растягиваем существующий
                           [Abs(MousePos.Y-MouseLDownPos.Y)]), gEditorFont);
       end
     else // Растягиваем существующий
@@ -2986,7 +2993,7 @@ begin
 
         PrintBlack(MousePos.X+2, MousePos.Y+2, Format(_glc[I_HINT_WIDTH], [Width]),
                           gEditorFont);
 
         PrintBlack(MousePos.X+2, MousePos.Y+2, Format(_glc[I_HINT_WIDTH], [Width]),
                           gEditorFont);
-        PrintBlack(MousePos.X+2, MousePos.Y+14, Format(_glc[I_HINT_HEIGHT], [Height]),
+        PrintBlack(MousePos.X+2, MousePos.Y+16, Format(_glc[I_HINT_HEIGHT], [Height]),
                           gEditorFont);
       end;
   end;
                           gEditorFont);
       end;
   end;
@@ -3090,18 +3097,13 @@ procedure TMainForm.FormResize(Sender: TObject);
 begin
   e_SetViewPort(0, 0, RenderPanel.Width, RenderPanel.Height);
 
 begin
   e_SetViewPort(0, 0, RenderPanel.Width, RenderPanel.Height);
 
-  if gMapInfo.Width >= RenderPanel.Width then
-    sbHorizontal.Max := Normalize16(gMapInfo.Width-RenderPanel.Width+16)
-  else
-    sbHorizontal.Max := 0;
-
-  if gMapInfo.Height >= RenderPanel.Height then
-    sbVertical.Max := Normalize16(gMapInfo.Height-RenderPanel.Height+16)
-  else
-    sbVertical.Max := 0;
+  sbHorizontal.Min := Min(gMapInfo.Width - RenderPanel.Width, -RenderPanel.Width div 2);
+  sbHorizontal.Max := Max(0, gMapInfo.Width - RenderPanel.Width div 2);
+  sbVertical.Min := Min(gMapInfo.Height - RenderPanel.Height, -RenderPanel.Height div 2);
+  sbVertical.Max := Max(0, gMapInfo.Height - RenderPanel.Height div 2);
 
 
-  MapOffset.X := -Normalize16(sbHorizontal.Position);
-  MapOffset.Y := -Normalize16(sbVertical.Position);
+  MapOffset.X := -sbHorizontal.Position;
+  MapOffset.Y := -sbVertical.Position;
 end;
 
 procedure SelectNextObject(X, Y: Integer; ObjectType: Byte; ID: DWORD);
 end;
 
 procedure SelectNextObject(X, Y: Integer; ObjectType: Byte; ID: DWORD);
@@ -3592,6 +3594,16 @@ begin
         end;
   end; // if Button = mbRight
 
         end;
   end; // if Button = mbRight
 
+  if Button = mbMiddle then // Middle Mouse Button
+  begin
+    SetCapture(RenderPanel.Handle);
+    RenderPanel.Cursor := crSize;
+  end;
+
+  MouseMDown := Button = mbMiddle;
+  if MouseMDown then
+    MouseMDownPos := Mouse.CursorPos;
+
   MouseRDown := Button = mbRight;
   if MouseRDown then
     MouseRDownPos := MousePos;
   MouseRDown := Button = mbRight;
   if MouseRDown then
     MouseRDownPos := MousePos;
@@ -3606,26 +3618,46 @@ procedure TMainForm.RenderPanelMouseUp(Sender: TObject;
 var
   panel: TPanel;
   trigger: TTrigger;
 var
   panel: TPanel;
   trigger: TTrigger;
-  i: Integer;
-  IDArray: DWArray;
   rRect: TRectWH;
   rSelectRect: Boolean;
   rRect: TRectWH;
   rSelectRect: Boolean;
+  wWidth, wHeight: Word;
+  TextureID: DWORD;
+
+  procedure SelectObjects(ObjectType: Byte);
+  var
+    i: Integer;
+    IDArray: DWArray;
+  begin
+    IDArray := ObjectInRect(rRect.X, rRect.Y,
+                 rRect.Width, rRect.Height,
+                 ObjectType, rSelectRect);
+
+    if IDArray <> nil then
+      for i := 0 to High(IDArray) do
+        SelectObject(ObjectType, IDArray[i], (ssCtrl in Shift) or rSelectRect);
+  end;
 begin
   if Button = mbLeft then
     MouseLDown := False;
   if Button = mbRight then
     MouseRDown := False;
 begin
   if Button = mbLeft then
     MouseLDown := False;
   if Button = mbRight then
     MouseRDown := False;
+  if Button = mbMiddle then
+    MouseMDown := False;
 
   DrawRect := nil;
   ResizeType := RESIZETYPE_NONE;
 
   DrawRect := nil;
   ResizeType := RESIZETYPE_NONE;
+  TextureID := 0;
 
   if Button = mbLeft then // Left Mouse Button
     begin
       if MouseAction <> MOUSEACTION_NONE then
       begin // Было действие мышью
 
   if Button = mbLeft then // Left Mouse Button
     begin
       if MouseAction <> MOUSEACTION_NONE then
       begin // Было действие мышью
-      // Мышь сдвинулась во время удержания клавиши:
-        if (MousePos.X <> MouseLDownPos.X) and
-           (MousePos.Y <> MouseLDownPos.Y) then
+      // Мышь сдвинулась во время удержания клавиши,
+      // либо активирован режим быстрого рисования:
+        if ((MousePos.X <> MouseLDownPos.X) and
+           (MousePos.Y <> MouseLDownPos.Y)) or
+           ((MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER]) and
+           (ssCtrl in Shift)) then
           case MouseAction of
           // Рисовали панель:
             MOUSEACTION_DRAWPANEL:
           case MouseAction of
           // Рисовали панель:
             MOUSEACTION_DRAWPANEL:
@@ -3655,8 +3687,25 @@ begin
 
                     Panel.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
                     Panel.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
 
                     Panel.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
                     Panel.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
-                    Panel.Width := Abs(MousePos.X-MouseLDownPos.X);
-                    Panel.Height := Abs(MousePos.Y-MouseLDownPos.Y);
+                    if ssCtrl in Shift then
+                      begin
+                        wWidth := DotStep;
+                        wHeight := DotStep;
+                        if (lbTextureList.ItemIndex <> -1) and
+                           (not IsSpecialTextureSel()) then
+                        begin
+                          if not g_GetTexture(SelectedTexture(), TextureID) then
+                            g_GetTexture('NOTEXTURE', TextureID);
+                          g_GetTextureSizeByID(TextureID, wWidth, wHeight);
+                        end;
+                        Panel.Width := wWidth;
+                        Panel.Height := wHeight;
+                      end
+                    else
+                      begin
+                        Panel.Width := Abs(MousePos.X-MouseLDownPos.X);
+                        Panel.Height := Abs(MousePos.Y-MouseLDownPos.Y);
+                      end;
 
                   // Лифты, блокМон или отсутствие текстуры - пустая текстура:
                     if (lbPanelType.ItemIndex in [9, 10, 11, 12, 13]) or
 
                   // Лифты, блокМон или отсутствие текстуры - пустая текстура:
                     if (lbPanelType.ItemIndex in [9, 10, 11, 12, 13]) or
@@ -3698,8 +3747,18 @@ begin
               begin
                 trigger.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
                 trigger.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
               begin
                 trigger.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
                 trigger.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
-                trigger.Width := Abs(MousePos.X-MouseLDownPos.X);
-                trigger.Height := Abs(MousePos.Y-MouseLDownPos.Y);
+                if ssCtrl in Shift then
+                  begin
+                    wWidth := DotStep;
+                    wHeight := DotStep;
+                    trigger.Width := wWidth;
+                    trigger.Height := wHeight;
+                  end
+                else
+                  begin
+                    trigger.Width := Abs(MousePos.X-MouseLDownPos.X);
+                    trigger.Height := Abs(MousePos.Y-MouseLDownPos.Y);
+                  end;
 
                 trigger.Enabled := True;
                 trigger.TriggerType := lbTriggersList.ItemIndex+1;
 
                 trigger.Enabled := True;
                 trigger.TriggerType := lbTriggersList.ItemIndex+1;
@@ -3893,7 +3952,7 @@ begin
         MouseAction := MOUSEACTION_NONE;
       end;
     end // if Button = mbLeft...
         MouseAction := MOUSEACTION_NONE;
       end;
     end // if Button = mbLeft...
-  else // Right Mouse Button:
+  else if Button = mbRight then // Right Mouse Button:
     begin
       if MouseAction = MOUSEACTION_NOACTION then
       begin
     begin
       if MouseAction = MOUSEACTION_NOACTION then
       begin
@@ -3904,6 +3963,7 @@ begin
     // Объект передвинут или изменен в размере:
       if MouseAction in [MOUSEACTION_MOVEOBJ, MOUSEACTION_RESIZE] then
       begin
     // Объект передвинут или изменен в размере:
       if MouseAction in [MOUSEACTION_MOVEOBJ, MOUSEACTION_RESIZE] then
       begin
+        RenderPanel.Cursor := crDefault;
         MouseAction := MOUSEACTION_NONE;
         FillProperty();
         Exit;
         MouseAction := MOUSEACTION_NONE;
         FillProperty();
         Exit;
@@ -3944,16 +4004,24 @@ begin
         RemoveSelectFromObjects();
 
     // Выделяем всё в выбранном прямоугольнике:
         RemoveSelectFromObjects();
 
     // Выделяем всё в выбранном прямоугольнике:
-      IDArray := ObjectInRect(rRect.X, rRect.Y,
-                   rRect.Width, rRect.Height,
-                   pcObjects.ActivePageIndex+1, rSelectRect);
-
-      if IDArray <> nil then
-        for i := 0 to High(IDArray) do
-          SelectObject(pcObjects.ActivePageIndex+1, IDArray[i],
-            (ssCtrl in Shift) or rSelectRect);
+      if (ssCtrl in Shift) and (ssAlt in Shift) then
+        begin
+          SelectObjects(OBJECT_PANEL);
+          SelectObjects(OBJECT_ITEM);
+          SelectObjects(OBJECT_MONSTER);
+          SelectObjects(OBJECT_AREA);
+          SelectObjects(OBJECT_TRIGGER);
+        end
+      else
+        SelectObjects(pcObjects.ActivePageIndex+1);
 
       FillProperty();
 
       FillProperty();
+    end
+
+  else // Middle Mouse Button
+    begin
+      RenderPanel.Cursor := crDefault;
+      ReleaseCapture();
     end;
 end;
 
     end;
 end;
 
@@ -3962,14 +4030,30 @@ begin
   Draw();
 end;
 
   Draw();
 end;
 
+function TMainForm.RenderMousePos(): Types.TPoint;
+begin
+  Result := RenderPanel.ScreenToClient(Mouse.CursorPos);
+end;
+
+procedure TMainForm.RecountSelectedObjects();
+begin
+  if SelectedObjectCount() = 0 then
+    StatusBar.Panels[0].Text := ''
+  else
+    StatusBar.Panels[0].Text := Format(_lc[I_CAP_STAT_SELECTED], [SelectedObjectCount()]);
+end;
+
 procedure TMainForm.RenderPanelMouseMove(Sender: TObject;
   Shift: TShiftState; X, Y: Integer);
 var
   sX, sY: Integer;
   dWidth, dHeight: Integer;
   _id: Integer;
 procedure TMainForm.RenderPanelMouseMove(Sender: TObject;
   Shift: TShiftState; X, Y: Integer);
 var
   sX, sY: Integer;
   dWidth, dHeight: Integer;
   _id: Integer;
+  TextureID: DWORD;
+  wWidth, wHeight: Word;
 begin
   _id := GetFirstSelected();
 begin
   _id := GetFirstSelected();
+  TextureID := 0;
 
 // Рисуем панель с текстурой, сетка - размеры текстуры:
   if (MouseAction = MOUSEACTION_DRAWPANEL) and
 
 // Рисуем панель с текстурой, сетка - размеры текстуры:
   if (MouseAction = MOUSEACTION_DRAWPANEL) and
@@ -4018,16 +4102,12 @@ begin
       end
     else
       begin // Кнопки мыши не зажаты
       end
     else
       begin // Кнопки мыши не зажаты
-        MousePos.X := (Round(X/sX)*sX);
-        MousePos.Y := (Round(Y/sY)*sY);
+        MousePos.X := Round((-MapOffset.X + X) / sX) * sX + MapOffset.X;
+        MousePos.Y := Round((-MapOffset.Y + Y) / sY) * sY + MapOffset.Y;
       end;
 
       end;
 
-// Изменение размера закончилось - ставим обычный курсор:
-  if ResizeType = RESIZETYPE_NONE then
-    RenderPanel.Cursor := crDefault;
-
 // Зажата только правая кнопка мыши:
 // Зажата только правая кнопка мыши:
-  if (not MouseLDown) and (MouseRDown) then
+  if (not MouseLDown) and (MouseRDown) and (not MouseMDown) then
   begin
   // Рисуем прямоугольник выделения:
     if MouseAction = MOUSEACTION_NONE then
   begin
   // Рисуем прямоугольник выделения:
     if MouseAction = MOUSEACTION_NONE then
@@ -4044,10 +4124,8 @@ begin
       if MouseAction = MOUSEACTION_MOVEOBJ then
         begin
           MoveSelectedObjects(ssShift in Shift, ssCtrl in Shift,
       if MouseAction = MOUSEACTION_MOVEOBJ then
         begin
           MoveSelectedObjects(ssShift in Shift, ssCtrl in Shift,
-                              MousePos.X-LastMovePoint.X+WASDOffset.X,
-                              MousePos.Y-LastMovePoint.Y+WASDOffset.Y);
-          WASDOffset.X := 0;
-          WASDOffset.Y := 0;
+                              MousePos.X-LastMovePoint.X,
+                              MousePos.Y-LastMovePoint.Y);
         end
       else
       // Меняем размер выделенного объекта:
         end
       else
       // Меняем размер выделенного объекта:
@@ -4056,10 +4134,8 @@ begin
           if (SelectedObjectCount = 1) and
              (SelectedObjects[GetFirstSelected].Live) then
           begin
           if (SelectedObjectCount = 1) and
              (SelectedObjects[GetFirstSelected].Live) then
           begin
-            dWidth := MousePos.X-LastMovePoint.X+WASDOffset.X;
-            dHeight := MousePos.Y-LastMovePoint.Y+WASDOffset.Y;
-            WASDOffset.X := 0;
-            WASDOffset.Y := 0;
+            dWidth := MousePos.X-LastMovePoint.X;
+            dHeight := MousePos.Y-LastMovePoint.Y;
 
             case ResizeType of
               RESIZETYPE_VERTICAL: dWidth := 0;
 
             case ResizeType of
               RESIZETYPE_VERTICAL: dWidth := 0;
@@ -4071,17 +4147,16 @@ begin
               RESIZEDIR_LEFT: dWidth := -dWidth;
             end;
 
               RESIZEDIR_LEFT: dWidth := -dWidth;
             end;
 
-            ResizeObject(SelectedObjects[GetFirstSelected].ObjectType,
-                         SelectedObjects[GetFirstSelected].ID,
-                         dWidth, dHeight, ResizeDirection);
-
-            LastMovePoint := MousePos;
+            if ResizeObject(SelectedObjects[GetFirstSelected].ObjectType,
+                           SelectedObjects[GetFirstSelected].ID,
+                           dWidth, dHeight, ResizeDirection) then
+              LastMovePoint := MousePos;
           end;
         end;
   end;
 
 // Зажата только левая кнопка мыши:
           end;
         end;
   end;
 
 // Зажата только левая кнопка мыши:
-  if (not MouseRDown) and (MouseLDown) then
+  if (not MouseRDown) and (MouseLDown) and (not MouseMDown) then
   begin
   // Рисуем прямоугольник планирования панели:
     if MouseAction in [MOUSEACTION_DRAWPANEL,
   begin
   // Рисуем прямоугольник планирования панели:
     if MouseAction in [MOUSEACTION_DRAWPANEL,
@@ -4090,10 +4165,29 @@ begin
       begin
         if DrawRect = nil then
           New(DrawRect);
       begin
         if DrawRect = nil then
           New(DrawRect);
-        DrawRect.Top := MouseLDownPos.y;
-        DrawRect.Left := MouseLDownPos.x;
-        DrawRect.Bottom := MousePos.y;
-        DrawRect.Right := MousePos.x;
+        if ssCtrl in Shift then
+          begin
+            wWidth := DotStep;
+            wHeight := DotStep;
+            if (lbTextureList.ItemIndex <> -1) and (not IsSpecialTextureSel()) and
+               (MouseAction = MOUSEACTION_DRAWPANEL) then
+            begin
+              if not g_GetTexture(SelectedTexture(), TextureID) then
+                g_GetTexture('NOTEXTURE', TextureID);
+              g_GetTextureSizeByID(TextureID, wWidth, wHeight);
+            end;
+            DrawRect.Top := MouseLDownPos.y;
+            DrawRect.Left := MouseLDownPos.x;
+            DrawRect.Bottom := DrawRect.Top + wHeight;
+            DrawRect.Right := DrawRect.Left + wWidth;
+          end
+        else
+          begin
+            DrawRect.Top := MouseLDownPos.y;
+            DrawRect.Left := MouseLDownPos.x;
+            DrawRect.Bottom := MousePos.y;
+            DrawRect.Right := MousePos.x;
+          end;
       end
     else // Двигаем карту:
       if MouseAction = MOUSEACTION_MOVEMAP then
       end
     else // Двигаем карту:
       if MouseAction = MOUSEACTION_MOVEMAP then
@@ -4102,6 +4196,18 @@ begin
       end;
   end;
 
       end;
   end;
 
+// Only Middle Mouse Button is pressed
+  if (not MouseLDown) and (not MouseRDown) and (MouseMDown) then
+  begin
+    MapOffset.X := -EnsureRange(-MapOffset.X + MouseMDownPos.X - Mouse.CursorPos.X,
+                                sbHorizontal.Min, sbHorizontal.Max);
+    sbHorizontal.Position := -MapOffset.X;
+    MapOffset.Y := -EnsureRange(-MapOffset.Y + MouseMDownPos.Y - Mouse.CursorPos.Y,
+                                sbVertical.Min, sbVertical.Max);
+    sbVertical.Position := -MapOffset.Y;
+    MouseMDownPos := Mouse.CursorPos;
+  end;
+
 // Клавиши мыши не зажаты:
   if (not MouseRDown) and (not MouseLDown) then
     DrawRect := nil;
 // Клавиши мыши не зажаты:
   if (not MouseRDown) and (not MouseLDown) then
     DrawRect := nil;
@@ -4158,6 +4264,8 @@ begin
   config.WriteInt('Editor', 'EdgeAlpha', gAlphaEdge);
   config.WriteInt('Editor', 'LineAlpha', gAlphaTriggerLine);
   config.WriteInt('Editor', 'TriggerAlpha', gAlphaTriggerArea);
   config.WriteInt('Editor', 'EdgeAlpha', gAlphaEdge);
   config.WriteInt('Editor', 'LineAlpha', gAlphaTriggerLine);
   config.WriteInt('Editor', 'TriggerAlpha', gAlphaTriggerArea);
+  config.WriteInt('Editor', 'MonsterRectAlpha', gAlphaMonsterRect);
+  config.WriteInt('Editor', 'AreaRectAlpha', gAlphaAreaRect);
 
   for i := 0 to RecentCount-1 do
     if i < RecentFiles.Count then
 
   for i := 0 to RecentCount-1 do
     if i < RecentFiles.Count then
@@ -4210,34 +4318,52 @@ begin
   AboutForm.ShowModal();
 end;
 
   AboutForm.ShowModal();
 end;
 
-procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
-  Shift: TShiftState);
+procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
 var
   dx, dy, i: Integer;
   FileName: String;
 var
   dx, dy, i: Integer;
   FileName: String;
+  ok: Boolean;
 begin
   if (not EditingProperties) then
   begin
 begin
   if (not EditingProperties) then
   begin
-    if Key = Ord('1') then
-      SwitchLayer(LAYER_BACK);
-    if Key = Ord('2') then
-      SwitchLayer(LAYER_WALLS);
-    if Key = Ord('3') then
-      SwitchLayer(LAYER_FOREGROUND);
-    if Key = Ord('4') then
-      SwitchLayer(LAYER_STEPS);
-    if Key = Ord('5') then
-      SwitchLayer(LAYER_WATER);
-    if Key = Ord('6') then
-      SwitchLayer(LAYER_ITEMS);
-    if Key = Ord('7') then
-      SwitchLayer(LAYER_MONSTERS);
-    if Key = Ord('8') then
-      SwitchLayer(LAYER_AREAS);
-    if Key = Ord('9') then
-      SwitchLayer(LAYER_TRIGGERS);
-    if Key = Ord('0') then
-      tbShowClick(tbShow);
+    if ssCtrl in Shift then
+    begin
+      case Chr(Key) of
+        '1': ContourEnabled[LAYER_BACK] := not ContourEnabled[LAYER_BACK];
+        '2': ContourEnabled[LAYER_WALLS] := not ContourEnabled[LAYER_WALLS];
+        '3': ContourEnabled[LAYER_FOREGROUND] := not ContourEnabled[LAYER_FOREGROUND];
+        '4': ContourEnabled[LAYER_STEPS] := not ContourEnabled[LAYER_STEPS];
+        '5': ContourEnabled[LAYER_WATER] := not ContourEnabled[LAYER_WATER];
+        '6': ContourEnabled[LAYER_ITEMS] := not ContourEnabled[LAYER_ITEMS];
+        '7': ContourEnabled[LAYER_MONSTERS] := not ContourEnabled[LAYER_MONSTERS];
+        '8': ContourEnabled[LAYER_AREAS] := not ContourEnabled[LAYER_AREAS];
+        '9': ContourEnabled[LAYER_TRIGGERS] := not ContourEnabled[LAYER_TRIGGERS];
+        '0':
+           begin
+             ok := False;
+             for i := Low(ContourEnabled) to High(ContourEnabled) do
+               if ContourEnabled[i] then
+                 ok := True;
+             for i := Low(ContourEnabled) to High(ContourEnabled) do
+               ContourEnabled[i] := not ok
+           end
+      end
+    end
+    else
+    begin
+      case Chr(key) of
+        '1': SwitchLayer(LAYER_BACK);
+        '2': SwitchLayer(LAYER_WALLS);
+        '3': SwitchLayer(LAYER_FOREGROUND);
+        '4': SwitchLayer(LAYER_STEPS);
+        '5': SwitchLayer(LAYER_WATER);
+        '6': SwitchLayer(LAYER_ITEMS);
+        '7': SwitchLayer(LAYER_MONSTERS);
+        '8': SwitchLayer(LAYER_AREAS);
+        '9': SwitchLayer(LAYER_TRIGGERS);
+        '0': tbShowClick(tbShow);
+      end
+    end;
 
     if Key = Ord('V') then
     begin // Поворот монстров и областей:
 
     if Key = Ord('V') then
     begin // Поворот монстров и областей:
@@ -4290,24 +4416,42 @@ begin
       begin
         if Key = Ord('W') then
         begin
       begin
         if Key = Ord('W') then
         begin
-          if (MouseLDown or MouseRDown) and (Position >= DotStep) then
+          dy := Position;
+          if ssShift in Shift then Position := EnsureRange(Position - DotStep * 4, Min, Max)
+          else Position := EnsureRange(Position - DotStep, Min, Max);
+          MapOffset.Y := -Position;
+          dy -= Position;
+
+          if (MouseLDown or MouseRDown) then
           begin
           begin
-            Dec(WASDOffset.Y, DotStep);
-            RenderPanelMouseMove(Sender, Shift, LastMovePoint.X, LastMovePoint.Y);
+            if DrawRect <> nil then
+            begin
+              Inc(MouseLDownPos.y, dy);
+              Inc(MouseRDownPos.y, dy);
+            end;
+            Inc(LastMovePoint.Y, dy);
+            RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
           end;
           end;
-          Position := IfThen(Position > DotStep, Position-DotStep, 0);
-          MapOffset.Y := -Round(Position/16) * 16;
         end;
 
         if Key = Ord('S') then
         begin
         end;
 
         if Key = Ord('S') then
         begin
-          if (MouseLDown or MouseRDown) and (Position+DotStep <= Max) then
+          dy := Position;
+          if ssShift in Shift then Position := EnsureRange(Position + DotStep * 4, Min, Max)
+          else Position := EnsureRange(Position + DotStep, Min, Max);
+          MapOffset.Y := -Position;
+          dy -= Position;
+
+          if (MouseLDown or MouseRDown) then
           begin
           begin
-            Inc(WASDOffset.Y, DotStep);
-            RenderPanelMouseMove(Sender, Shift, LastMovePoint.X, LastMovePoint.Y);
+            if DrawRect <> nil then
+            begin
+              Inc(MouseLDownPos.y, dy);
+              Inc(MouseRDownPos.y, dy);
+            end;
+            Inc(LastMovePoint.Y, dy);
+            RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
           end;
           end;
-          Position := IfThen(Position+DotStep < Max, Position+DotStep, Max);
-          MapOffset.Y := -Round(Position/16) * 16;
         end;
       end;
 
         end;
       end;
 
@@ -4316,24 +4460,42 @@ begin
       begin
         if Key = Ord('A') then
         begin
       begin
         if Key = Ord('A') then
         begin
-          if (MouseLDown or MouseRDown) and (Position >= DotStep) then
+          dx := Position;
+          if ssShift in Shift then Position := EnsureRange(Position - DotStep * 4, Min, Max)
+          else Position := EnsureRange(Position - DotStep, Min, Max);
+          MapOffset.X := -Position;
+          dx -= Position;
+
+          if (MouseLDown or MouseRDown) then
           begin
           begin
-            Dec(WASDOffset.X, DotStep);
-            RenderPanelMouseMove(Sender, Shift, LastMovePoint.X, LastMovePoint.Y);
+            if DrawRect <> nil then
+            begin
+              Inc(MouseLDownPos.x, dx);
+              Inc(MouseRDownPos.x, dx);
+            end;
+            Inc(LastMovePoint.X, dx);
+            RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
           end;
           end;
-          Position := IfThen(Position > DotStep, Position-DotStep, 0);
-          MapOffset.X := -Round(Position/16) * 16;
         end;
 
         if Key = Ord('D') then
         begin
         end;
 
         if Key = Ord('D') then
         begin
-          if (MouseLDown or MouseRDown) and (Position+DotStep <= Max) then
+          dx := Position;
+          if ssShift in Shift then Position := EnsureRange(Position + DotStep * 4, Min, Max)
+          else Position := EnsureRange(Position + DotStep, Min, Max);
+          MapOffset.X := -Position;
+          dx -= Position;
+
+          if (MouseLDown or MouseRDown) then
           begin
           begin
-            Inc(WASDOffset.X, DotStep);
-            RenderPanelMouseMove(Sender, Shift, LastMovePoint.X, LastMovePoint.Y);
+            if DrawRect <> nil then
+            begin
+              Inc(MouseLDownPos.x, dx);
+              Inc(MouseRDownPos.x, dx);
+            end;
+            Inc(LastMovePoint.X, dx);
+            RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
           end;
           end;
-          Position := IfThen(Position+DotStep < Max, Position+DotStep, Max);
-          MapOffset.X := -Round(Position/16) * 16;
         end;
       end;
     end
         end;
       end;
     end
@@ -4345,6 +4507,7 @@ begin
         if Key = Ord('V') then
           aPasteObjectExecute(Sender);
       end;
         if Key = Ord('V') then
           aPasteObjectExecute(Sender);
       end;
+      RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
     end;
   end;
 
     end;
   end;
 
@@ -4642,6 +4805,16 @@ begin
         Values.Add(_lc[I_PROP_TR_SHOT_AIM_2]);
         Values.Add(_lc[I_PROP_TR_SHOT_AIM_3]);
       end
         Values.Add(_lc[I_PROP_TR_SHOT_AIM_2]);
         Values.Add(_lc[I_PROP_TR_SHOT_AIM_3]);
       end
+    else if KeyName = _lc[I_PROP_TR_DAMAGE_KIND] then
+      begin
+        Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_0]);
+        Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_3]);
+        Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_4]);
+        Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_5]);
+        Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_6]);
+        Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_7]);
+        Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_8]);
+      end
     else if (KeyName = _lc[I_PROP_PANEL_BLEND]) or
             (KeyName = _lc[I_PROP_DM_ONLY]) or
             (KeyName = _lc[I_PROP_ITEM_FALLS]) or
     else if (KeyName = _lc[I_PROP_PANEL_BLEND]) or
             (KeyName = _lc[I_PROP_DM_ONLY]) or
             (KeyName = _lc[I_PROP_ITEM_FALLS]) or
@@ -5045,6 +5218,21 @@ begin
                   StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_DAMAGE_VALUE]], 0), 0), 65535);
                 Data.DamageInterval := Min(Max(
                   StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_INTERVAL]], 0), 0), 65535);
                   StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_DAMAGE_VALUE]], 0), 0), 65535);
                 Data.DamageInterval := Min(Max(
                   StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_INTERVAL]], 0), 0), 65535);
+                s := vleObjectProperty.Values[_lc[I_PROP_TR_DAMAGE_KIND]];
+                if s = _lc[I_PROP_TR_DAMAGE_KIND_3] then
+                  Data.DamageKind := 3
+                else if s = _lc[I_PROP_TR_DAMAGE_KIND_4] then
+                  Data.DamageKind := 4
+                else if s = _lc[I_PROP_TR_DAMAGE_KIND_5] then
+                  Data.DamageKind := 5
+                else if s = _lc[I_PROP_TR_DAMAGE_KIND_6] then
+                  Data.DamageKind := 6
+                else if s = _lc[I_PROP_TR_DAMAGE_KIND_7] then
+                  Data.DamageKind := 7
+                else if s = _lc[I_PROP_TR_DAMAGE_KIND_8] then
+                  Data.DamageKind := 8
+                else
+                  Data.DamageKind := 0;
               end;
 
             TRIGGER_HEALTH:
               end;
 
             TRIGGER_HEALTH:
@@ -6057,69 +6245,48 @@ end;
 
 procedure TMainForm.aDeleteMap(Sender: TObject);
 var
 
 procedure TMainForm.aDeleteMap(Sender: TObject);
 var
-  WAD: TWADEditor_1;
-  MapList: SArray;
-  MapName: Char16;
-  a: Integer;
-  str: String;
+  res: Integer;
+  FileName: String;
+  MapName: String;
 begin
   OpenDialog.Filter := _lc[I_FILE_FILTER_WAD];
 
   if not OpenDialog.Execute() then
     Exit;
 
 begin
   OpenDialog.Filter := _lc[I_FILE_FILTER_WAD];
 
   if not OpenDialog.Execute() then
     Exit;
 
-  WAD := TWADEditor_1.Create();
-
-  if not WAD.ReadFile(OpenDialog.FileName) then
-  begin
-    WAD.Free();
-    Exit;
-  end;
-
-  WAD.CreateImage();
-
-  MapList := WAD.GetResourcesList('');
-
+  FileName := OpenDialog.FileName;
   SelectMapForm.Caption := _lc[I_CAP_REMOVE];
   SelectMapForm.lbMapList.Items.Clear();
   SelectMapForm.Caption := _lc[I_CAP_REMOVE];
   SelectMapForm.lbMapList.Items.Clear();
+  SelectMapForm.GetMaps(FileName);
 
 
-  if MapList <> nil then
-    for a := 0 to High(MapList) do
-      SelectMapForm.lbMapList.Items.Add(win2utf(MapList[a]));
+  if SelectMapForm.ShowModal() <> mrOK then
+    Exit;
 
 
-  if (SelectMapForm.ShowModal() = mrOK) then
-  begin
-    str := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
-    MapName := '';
-    Move(str[1], MapName[0], Min(16, Length(str)));
-
-    if MessageBox(0, PChar(Format(_lc[I_MSG_DELETE_MAP_PROMT],
-                           [MapName, OpenDialog.FileName])),
-                  PChar(_lc[I_MSG_DELETE_MAP]),
-                  MB_ICONQUESTION or MB_YESNO or
-                  MB_DEFBUTTON2) <> mrYes then
-      Exit;
+  MapName := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
+  if MessageBox(0, PChar(Format(_lc[I_MSG_DELETE_MAP_PROMT], [MapName, OpenDialog.FileName])), PChar(_lc[I_MSG_DELETE_MAP]), MB_ICONQUESTION or MB_YESNO or MB_DEFBUTTON2) <> mrYes then
+    Exit;
 
 
-    WAD.RemoveResource('', utf2win(MapName));
-    
-    MessageBox(0, PChar(Format(_lc[I_MSG_MAP_DELETED_PROMT],
-                               [MapName])),
-               PChar(_lc[I_MSG_MAP_DELETED]),
-               MB_ICONINFORMATION or MB_OK or
-               MB_DEFBUTTON1);
+  g_DeleteResource(FileName, '', MapName, res);
+  if res <> 0 then
+  begin
+    MessageBox(0, PChar('Cant delete map res=' + IntToStr(res)), PChar('Map not deleted!'), MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1);
+    Exit
+  end;
 
 
-    WAD.SaveTo(OpenDialog.FileName);
+  MessageBox(
+    0,
+    PChar(Format(_lc[I_MSG_MAP_DELETED_PROMT], [MapName])),
+    PChar(_lc[I_MSG_MAP_DELETED]),
+    MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1
+  );
 
   // Удалили текущую карту - сохранять по старому ее нельзя:
 
   // Удалили текущую карту - сохранять по старому ее нельзя:
-    if OpenedMap = (OpenDialog.FileName+':\'+MapName) then
-    begin
-      OpenedMap := '';
-      OpenedWAD := '';
-      MainForm.Caption := FormCaption;
-    end;
-  end;
-
-  WAD.Free();
+  if OpenedMap = (FileName + ':\' + MapName) then
+  begin
+    OpenedMap := '';
+    OpenedWAD := '';
+    MainForm.Caption := FormCaption
+  end
 end;
 
 procedure TMainForm.vleObjectPropertyKeyDown(Sender: TObject;
 end;
 
 procedure TMainForm.vleObjectPropertyKeyDown(Sender: TObject;
@@ -6334,6 +6501,8 @@ begin
           if gTriggers[a].TriggerType <> TRIGGER_NONE then
             SelectObject(OBJECT_TRIGGER, a, True);
   end;
           if gTriggers[a].TriggerType <> TRIGGER_NONE then
             SelectObject(OBJECT_TRIGGER, a, True);
   end;
+
+  RecountSelectedObjects();
 end;
 
 procedure TMainForm.tbGridOnClick(Sender: TObject);
 end;
 
 procedure TMainForm.tbGridOnClick(Sender: TObject);
@@ -6522,35 +6691,65 @@ begin
   MapTestForm.ShowModal();
 end;
 
   MapTestForm.ShowModal();
 end;
 
+type SSArray = array of String;
+
+function ParseString (Str: AnsiString): SSArray;
+  function GetStr (var Str: AnsiString): AnsiString;
+    var a, b: Integer;
+  begin
+    Result := '';
+    if Str[1] = '"' then
+      for b := 1 to Length(Str) do
+        if (b = Length(Str)) or (Str[b + 1] = '"') then
+        begin
+          Result := Copy(Str, 2, b - 1);
+          Delete(Str, 1, b + 1);
+          Str := Trim(Str);
+          Exit;
+        end;
+    for a := 1 to Length(Str) do
+      if (a = Length(Str)) or (Str[a + 1] = ' ') then
+      begin
+        Result := Copy(Str, 1, a);
+        Delete(Str, 1, a + 1);
+        Str := Trim(Str);
+        Exit;
+      end;
+  end;
+begin
+  Result := nil;
+  Str := Trim(Str);
+  while Str <> '' do
+  begin
+    SetLength(Result, Length(Result)+1);
+    Result[High(Result)] := GetStr(Str);
+  end;
+end;
+
 procedure TMainForm.miTestMapClick(Sender: TObject);
 var
 procedure TMainForm.miTestMapClick(Sender: TObject);
 var
-  cmd, mapWAD, mapToRun, tempWAD: String;
+  newWAD, oldWAD, tempMap: String;
+  args: SSArray;
   opt: LongWord;
   opt: LongWord;
-  time: Integer;
+  time, i: Integer;
   proc: TProcessUTF8;
   res: Boolean;
 begin
   proc: TProcessUTF8;
   res: Boolean;
 begin
-  mapToRun := '';
-  if OpenedMap <> '' then
-  begin
-    // Указываем текущую карту для теста:
-    g_ProcessResourceStr(OpenedMap, @mapWAD, nil, @mapToRun);
-    mapToRun := mapWAD + ':\' + mapToRun;
-    mapToRun := ExtractRelativePath(ExtractFilePath(TestD2dExe) + 'maps/', mapToRun);
-  end;
   // Сохраняем временную карту:
   time := 0;
   repeat
   // Сохраняем временную карту:
   time := 0;
   repeat
-    mapWAD := ExtractFilePath(TestD2dExe) + Format('maps/temp%.4d.wad', [time]);
+    newWAD := ExtractFilePath(TestD2dExe) + Format('maps/temp%.4d.wad', [time]);
     Inc(time);
     Inc(time);
-  until not FileExists(mapWAD);
-  tempWAD := mapWAD + ':\' + TEST_MAP_NAME;
-  SaveMap(tempWAD);
-
-  tempWAD := ExtractRelativePath(ExtractFilePath(TestD2dExe) + 'maps/', tempWAD);
-// Если карта не была открыта, указываем временную в качестве текущей:
-  if mapToRun = '' then
-    mapToRun := tempWAD;
+  until not FileExists(newWAD);
+  if OpenedMap <> '' then
+  begin
+    oldWad := g_ExtractWadName(OpenedMap);
+    if CopyFile(oldWad, newWad) = false then
+      e_WriteLog('MapTest: unable to copy [' + oldWad + '] to [' + newWad + ']', MSG_WARNING)
+  end;
+  tempMap := newWAD + ':\' + TEST_MAP_NAME;
+  SaveMap(tempMap);
+  tempMap := ExtractRelativePath(ExtractFilePath(TestD2dExe) + 'maps/', tempMap);
 
 // Опции игры:
   opt := 32 + 64;
 
 // Опции игры:
   opt := 32 + 64;
@@ -6565,23 +6764,27 @@ begin
   if TestOptionsMonstersDM then
     opt := opt + 16;
 
   if TestOptionsMonstersDM then
     opt := opt + 16;
 
-// Составляем командную строку:
-  cmd := '-map "' + mapToRun + '"';
-  cmd := cmd + ' -testmap "' + tempWAD + '"';
-  cmd := cmd + ' -gm ' + TestGameMode;
-  cmd := cmd + ' -limt ' + TestLimTime;
-  cmd := cmd + ' -lims ' + TestLimScore;
-  cmd := cmd + ' -opt ' + IntToStr(opt);
-
-  if TestMapOnce then
-    cmd := cmd + ' --close';
-
-  cmd := cmd + ' --debug';
-
 // Запускаем:
   proc := TProcessUTF8.Create(nil);
   proc.Executable := TestD2dExe;
 // Запускаем:
   proc := TProcessUTF8.Create(nil);
   proc.Executable := TestD2dExe;
-  proc.Parameters.Add(cmd);
+  proc.Parameters.Add('-map');
+  proc.Parameters.Add(tempMap);
+  proc.Parameters.Add('-gm');
+  proc.Parameters.Add(TestGameMode);
+  proc.Parameters.Add('-limt');
+  proc.Parameters.Add(TestLimTime);
+  proc.Parameters.Add('-lims');
+  proc.Parameters.Add(TestLimScore);
+  proc.Parameters.Add('-opt');
+  proc.Parameters.Add(IntToStr(opt));
+  proc.Parameters.Add('--debug');
+  if TestMapOnce then
+    proc.Parameters.Add('--close');
+
+  args := ParseString(TestD2DArgs);
+  for i := 0 to High(args) do
+    proc.Parameters.Add(args[i]);
+
   res := True;
   try
     proc.Execute();
   res := True;
   try
     proc.Execute();
@@ -6601,20 +6804,20 @@ begin
   end;
   proc.Free();
 
   end;
   proc.Free();
 
-  SysUtils.DeleteFile(mapWAD);
+  SysUtils.DeleteFile(newWAD);
   Application.Restore();
 end;
 
 procedure TMainForm.sbVerticalScroll(Sender: TObject;
   ScrollCode: TScrollCode; var ScrollPos: Integer);
 begin
   Application.Restore();
 end;
 
 procedure TMainForm.sbVerticalScroll(Sender: TObject;
   ScrollCode: TScrollCode; var ScrollPos: Integer);
 begin
-  MapOffset.Y := -Normalize16(sbVertical.Position);
+  MapOffset.Y := -sbVertical.Position;
 end;
 
 procedure TMainForm.sbHorizontalScroll(Sender: TObject;
   ScrollCode: TScrollCode; var ScrollPos: Integer);
 begin
 end;
 
 procedure TMainForm.sbHorizontalScroll(Sender: TObject;
   ScrollCode: TScrollCode; var ScrollPos: Integer);
 begin
-  MapOffset.X := -Normalize16(sbHorizontal.Position);
+  MapOffset.X := -sbHorizontal.Position;
 end;
 
 procedure TMainForm.miOpenWadMapClick(Sender: TObject);
 end;
 
 procedure TMainForm.miOpenWadMapClick(Sender: TObject);
@@ -6655,6 +6858,8 @@ begin
     for a := 0 to High(gTriggers) do
       if gTriggers[a].TriggerType <> TRIGGER_NONE then
         SelectObject(OBJECT_TRIGGER, a, True);
     for a := 0 to High(gTriggers) do
       if gTriggers[a].TriggerType <> TRIGGER_NONE then
         SelectObject(OBJECT_TRIGGER, a, True);
+
+  RecountSelectedObjects();
 end;
 
 procedure TMainForm.Splitter1CanResize(Sender: TObject;
 end;
 
 procedure TMainForm.Splitter1CanResize(Sender: TObject;
@@ -6679,8 +6884,7 @@ begin
   EditingProperties := False;
 end;
 
   EditingProperties := False;
 end;
 
-procedure TMainForm.FormKeyUp(Sender: TObject; var Key: Word;
-  Shift: TShiftState);
+procedure TMainForm.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
 begin
 // Объекты передвигались:
   if MainForm.ActiveControl = RenderPanel then
 begin
 // Объекты передвигались:
   if MainForm.ActiveControl = RenderPanel then
@@ -6698,6 +6902,7 @@ begin
     if PreviewMode = 2 then
       PreviewMode := 0;
   end;
     if PreviewMode = 2 then
       PreviewMode := 0;
   end;
+  RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
 end;
 
 end.
 end;
 
 end.