DEADSOFTWARE

save temp wad with ext from opened wad
[d2df-editor.git] / src / editor / f_main.pas
index 95d468df1917067c9c67c1b4bb77d8423009a1e8..225daecbb528255b4ad1c689dcdb14b2e59d705d 100644 (file)
@@ -8,7 +8,7 @@ uses
   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
 
@@ -292,8 +292,8 @@ var
 
   DotColor: TColor;
   DotEnable: Boolean;
-  DotStep: Byte;
-  DotStepOne, DotStepTwo: Byte;
+  DotStep: Word;
+  DotStepOne, DotStepTwo: Word;
   DotSize: Byte;
   DrawTexturePanel: Boolean;
   DrawPanelSize: Boolean;
@@ -313,11 +313,13 @@ var
   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);
+  ContourEnabled: Array [LAYER_BACK..LAYER_TRIGGERS] of Boolean =
+    (False, False, False, False, False, False, False, False, False);
   PreviewMode: Byte = 0;
   gLanguage: String;
 
@@ -335,7 +337,7 @@ 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_activationtype, f_keys,
+  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,
@@ -440,8 +442,10 @@ var
   LastMovePoint: Types.TPoint;
   MouseLDown: Boolean;
   MouseRDown: Boolean;
+  MouseMDown: Boolean;
   MouseLDownPos: Types.TPoint;
   MouseRDownPos: Types.TPoint;
+  MouseMDownPos: Types.TPoint;
 
   SelectFlag: Byte = SELECTFLAG_NONE;
   MouseAction: Byte = MOUSEACTION_NONE;
@@ -692,10 +696,10 @@ begin
   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;
@@ -704,17 +708,11 @@ begin
     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:
-    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;
@@ -1408,6 +1406,20 @@ begin
                   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:
@@ -2567,45 +2579,27 @@ end;
 
 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;
-
-  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
-  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
-      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);
@@ -2657,6 +2651,10 @@ begin
   EditorDir := ExtractFilePath(Application.ExeName);
 
   e_InitLog(EditorDir+'Editor.log', WM_NEWFILE);
+  e_WriteLog('Doom 2D: Forever Editor version ' + EDITOR_VERSION, MSG_NOTIFY);
+  e_WriteLog('Build date: ' + EDITOR_BUILDDATE + ' ' + EDITOR_BUILDTIME, MSG_NOTIFY);
+  e_WriteLog('Build hash: ' + g_GetBuildHash(), MSG_NOTIFY);
+  e_WriteLog('Build by: ' + g_GetBuilderName(), MSG_NOTIFY);
 
   slInvalidTextures := TStringList.Create;
 
@@ -2837,12 +2835,22 @@ begin
     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));
+        y += DotStep;
+      end;
+      x += DotStep;
+      y := MapOffset.Y mod DotStep;
+    end;
   end;
 
 // Превью текстуры:
@@ -3093,18 +3101,13 @@ procedure TMainForm.FormResize(Sender: TObject);
 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);
@@ -3595,6 +3598,16 @@ begin
         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;
@@ -3632,6 +3645,8 @@ begin
     MouseLDown := False;
   if Button = mbRight then
     MouseRDown := False;
+  if Button = mbMiddle then
+    MouseMDown := False;
 
   DrawRect := nil;
   ResizeType := RESIZETYPE_NONE;
@@ -3941,7 +3956,7 @@ begin
         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
@@ -3952,6 +3967,7 @@ begin
     // Объект передвинут или изменен в размере:
       if MouseAction in [MOUSEACTION_MOVEOBJ, MOUSEACTION_RESIZE] then
       begin
+        RenderPanel.Cursor := crDefault;
         MouseAction := MOUSEACTION_NONE;
         FillProperty();
         Exit;
@@ -4004,6 +4020,12 @@ begin
         SelectObjects(pcObjects.ActivePageIndex+1);
 
       FillProperty();
+    end
+
+  else // Middle Mouse Button
+    begin
+      RenderPanel.Cursor := crDefault;
+      ReleaseCapture();
     end;
 end;
 
@@ -4084,16 +4106,12 @@ 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;
 
-// Изменение размера закончилось - ставим обычный курсор:
-  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
@@ -4142,7 +4160,7 @@ begin
   end;
 
 // Зажата только левая кнопка мыши:
-  if (not MouseRDown) and (MouseLDown) then
+  if (not MouseRDown) and (MouseLDown) and (not MouseMDown) then
   begin
   // Рисуем прямоугольник планирования панели:
     if MouseAction in [MOUSEACTION_DRAWPANEL,
@@ -4182,6 +4200,18 @@ begin
       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;
@@ -4292,34 +4322,52 @@ begin
   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;
+  ok: Boolean;
 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 // Поворот монстров и областей:
@@ -4372,34 +4420,42 @@ 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
             if DrawRect <> nil then
             begin
-              Inc(MouseLDownPos.y, DotStep);
-              Inc(MouseRDownPos.y, DotStep);
+              Inc(MouseLDownPos.y, dy);
+              Inc(MouseRDownPos.y, dy);
             end;
-            Inc(LastMovePoint.Y, DotStep);
+            Inc(LastMovePoint.Y, dy);
             RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
           end;
-          Position := IfThen(Position > DotStep, Position-DotStep, 0);
-          MapOffset.Y := -Round(Position/16) * 16;
         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
             if DrawRect <> nil then
             begin
-              Dec(MouseLDownPos.y, DotStep);
-              Dec(MouseRDownPos.y, DotStep);
+              Inc(MouseLDownPos.y, dy);
+              Inc(MouseRDownPos.y, dy);
             end;
-            Dec(LastMovePoint.Y, DotStep);
+            Inc(LastMovePoint.Y, dy);
             RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
           end;
-          Position := IfThen(Position+DotStep < Max, Position+DotStep, Max);
-          MapOffset.Y := -Round(Position/16) * 16;
         end;
       end;
 
@@ -4408,34 +4464,42 @@ 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
             if DrawRect <> nil then
             begin
-              Inc(MouseLDownPos.x, DotStep);
-              Inc(MouseRDownPos.x, DotStep);
+              Inc(MouseLDownPos.x, dx);
+              Inc(MouseRDownPos.x, dx);
             end;
-            Inc(LastMovePoint.X, DotStep);
+            Inc(LastMovePoint.X, dx);
             RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
           end;
-          Position := IfThen(Position > DotStep, Position-DotStep, 0);
-          MapOffset.X := -Round(Position/16) * 16;
         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
             if DrawRect <> nil then
             begin
-              Dec(MouseLDownPos.x, DotStep);
-              Dec(MouseRDownPos.x, DotStep);
+              Inc(MouseLDownPos.x, dx);
+              Inc(MouseRDownPos.x, dx);
             end;
-            Dec(LastMovePoint.X, DotStep);
+            Inc(LastMovePoint.X, dx);
             RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
           end;
-          Position := IfThen(Position+DotStep < Max, Position+DotStep, Max);
-          MapOffset.X := -Round(Position/16) * 16;
         end;
       end;
     end
@@ -4745,6 +4809,16 @@ begin
         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
@@ -5148,6 +5222,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);
+                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:
@@ -6606,35 +6695,70 @@ begin
   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
-  cmd, mapWAD, mapToRun, tempWAD: String;
+  newWAD, oldWAD, tempMap, ext: String;
+  args: SSArray;
   opt: LongWord;
-  time: Integer;
+  time, i: Integer;
   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
-    mapWAD := ExtractFilePath(TestD2dExe) + Format('maps/temp%.4d.wad', [time]);
+    newWAD := ExtractFilePath(TestD2dExe) + Format('maps/temp%.4d', [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);
+    newWad := newWad + ExtractFileExt(oldWad);
+    if CopyFile(oldWad, newWad) = false then
+      e_WriteLog('MapTest: unable to copy [' + oldWad + '] to [' + newWad + ']', MSG_WARNING)
+  end
+  else
+  begin
+    newWad := newWad + '.wad'
+  end;
+  tempMap := newWAD + ':\' + TEST_MAP_NAME;
+  SaveMap(tempMap);
+  tempMap := ExtractRelativePath(ExtractFilePath(TestD2dExe) + 'maps/', tempMap);
 
 // Опции игры:
   opt := 32 + 64;
@@ -6649,23 +6773,27 @@ begin
   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.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();
@@ -6685,20 +6813,20 @@ begin
   end;
   proc.Free();
 
-  SysUtils.DeleteFile(mapWAD);
+  SysUtils.DeleteFile(newWAD);
   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
-  MapOffset.X := -Normalize16(sbHorizontal.Position);
+  MapOffset.X := -sbHorizontal.Position;
 end;
 
 procedure TMainForm.miOpenWadMapClick(Sender: TObject);
@@ -6765,8 +6893,7 @@ begin
   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