DEADSOFTWARE

system: add option --log-file
[d2df-editor.git] / src / editor / f_main.pas
1 unit f_main;
3 {$INCLUDE ../shared/a_modes.inc}
5 interface
7 uses
8 LCLIntf, LCLType, SysUtils, Variants, Classes, Graphics,
9 Controls, Forms, Dialogs, StdCtrls, Buttons,
10 ComCtrls, ValEdit, Types, Menus, ExtCtrls,
11 CheckLst, Grids, OpenGLContext, Utils, UTF8Process;
13 type
15 { TMainForm }
17 TMainForm = class(TForm)
18 lLoad: TLabel;
19 // Главное меню:
20 MainMenu: TMainMenu;
21 // "Файл":
22 miMenuFile: TMenuItem;
23 miNewMap: TMenuItem;
24 miOpenMap: TMenuItem;
25 miSaveMap: TMenuItem;
26 miSaveMapAs: TMenuItem;
27 miOpenWadMap: TMenuItem;
28 miLine1: TMenuItem;
29 miReopenMap: TMenuItem;
30 miSaveMiniMap: TMenuItem;
31 miDeleteMap: TMenuItem;
32 miPackMap: TMenuItem;
33 miLine2: TMenuItem;
34 miExit: TMenuItem;
35 // "Правка":
36 miMenuEdit: TMenuItem;
37 miUndo: TMenuItem;
38 miLine3: TMenuItem;
39 miCopy: TMenuItem;
40 miCut: TMenuItem;
41 miPaste: TMenuItem;
42 miLine4: TMenuItem;
43 miSelectAll: TMenuItem;
44 miLine5: TMenuItem;
45 miToFore: TMenuItem;
46 miToBack: TMenuItem;
47 // "Инструменты":
48 miMenuTools: TMenuItem;
49 miSnapToGrid: TMenuItem;
50 miMiniMap: TMenuItem;
51 miSwitchGrid: TMenuItem;
52 miShowEdges: TMenuItem;
53 miLayers: TMenuItem;
54 miLayer1: TMenuItem;
55 miLayer2: TMenuItem;
56 miLayer3: TMenuItem;
57 miLayer4: TMenuItem;
58 miLayer5: TMenuItem;
59 miLayer6: TMenuItem;
60 miLayer7: TMenuItem;
61 miLayer8: TMenuItem;
62 miLayer9: TMenuItem;
63 // "Сервис":
64 miMenuService: TMenuItem;
65 miCheckMap: TMenuItem;
66 miOptimmization: TMenuItem;
67 miMapPreview: TMenuItem;
68 miTestMap: TMenuItem;
69 // "Настройка":
70 miMenuSettings: TMenuItem;
71 miMapOptions: TMenuItem;
72 miLine6: TMenuItem;
73 miOptions: TMenuItem;
74 miLine7: TMenuItem;
75 miMapTestSettings: TMenuItem;
76 // "Справка":
77 miMenuHelp: TMenuItem;
78 miAbout: TMenuItem;
79 // Скрытый пункт меню для Ctrl+Tab:
80 miHidden1: TMenuItem;
81 minexttab: TMenuItem;
83 // Панель инструментов:
84 MainToolBar: TToolBar;
85 pbLoad: TProgressBar;
86 pLoadProgress: TPanel;
87 RenderPanel: TOpenGLControl;
88 tbNewMap: TToolButton;
89 tbOpenMap: TToolButton;
90 tbSaveMap: TToolButton;
91 tbOpenWadMap: TToolButton;
92 tbLine1: TToolButton;
93 tbShowMap: TToolButton;
94 tbLine2: TToolButton;
95 tbShow: TToolButton;
96 tbLine3: TToolButton;
97 tbGridOn: TToolButton;
98 tbGrid: TToolButton;
99 tbLine4: TToolButton;
100 tbTestMap: TToolButton;
101 // Всплывающее меню для кнопки слоев:
102 pmShow: TPopupMenu;
103 miLayerP1: TMenuItem;
104 miLayerP2: TMenuItem;
105 miLayerP3: TMenuItem;
106 miLayerP4: TMenuItem;
107 miLayerP5: TMenuItem;
108 miLayerP6: TMenuItem;
109 miLayerP7: TMenuItem;
110 miLayerP8: TMenuItem;
111 miLayerP9: TMenuItem;
112 // Всплывающее меню для кнопки теста карты:
113 pmMapTest: TPopupMenu;
114 miMapTestPMSet: TMenuItem;
116 // Панель карты:
117 PanelMap: TPanel;
118 // Полосы прокрутки:
119 sbHorizontal: TScrollBar;
120 sbVertical: TScrollBar;
122 // Панель свойств:
123 PanelProps: TPanel;
124 // Панель применения свойств:
125 PanelPropApply: TPanel;
126 bApplyProperty: TButton;
127 // Редактор свойств объектов:
128 vleObjectProperty: TValueListEditor;
130 // Панель объектов - вкладки:
131 PanelObjs: TPanel;
132 pcObjects: TPageControl;
133 // Вкладка "Панели":
134 tsPanels: TTabSheet;
135 lbTextureList: TListBox;
136 // Панель настройки текстур:
137 PanelTextures: TPanel;
138 LabelTxW: TLabel;
139 lTextureWidth: TLabel;
140 LabelTxH: TLabel;
141 lTextureHeight: TLabel;
142 cbPreview: TCheckBox;
143 bbAddTexture: TBitBtn;
144 bbRemoveTexture: TBitBtn;
145 bClearTexture: TButton;
146 // Панель типов панелей:
147 PanelPanelType: TPanel;
148 lbPanelType: TListBox;
149 // Вкладка "Предметы":
150 tsItems: TTabSheet;
151 lbItemList: TListBox;
152 cbOnlyDM: TCheckBox;
153 cbFall: TCheckBox;
154 // Вкладка "Монстры":
155 tsMonsters: TTabSheet;
156 lbMonsterList: TListBox;
157 rbMonsterLeft: TRadioButton;
158 rbMonsterRight: TRadioButton;
159 // Вкладка "Области":
160 tsAreas: TTabSheet;
161 lbAreasList: TListBox;
162 rbAreaLeft: TRadioButton;
163 rbAreaRight: TRadioButton;
164 // Вкладка "Триггеры":
165 tsTriggers: TTabSheet;
166 lbTriggersList: TListBox;
167 clbActivationType: TCheckListBox;
168 clbKeys: TCheckListBox;
170 // Остальные панели
171 Splitter1: TSplitter;
172 Splitter2: TSplitter;
173 StatusBar: TStatusBar;
175 // Специальные объекты:
176 ImageList: TImageList;
177 ilToolbar: TImageList;
178 OpenDialog: TOpenDialog;
179 SaveDialog: TSaveDialog;
180 selectall1: TMenuItem;
181 ColorDialog: TColorDialog;
183 procedure aAboutExecute(Sender: TObject);
184 procedure aCheckMapExecute(Sender: TObject);
185 procedure aMoveToFore(Sender: TObject);
186 procedure aMoveToBack(Sender: TObject);
187 procedure aCopyObjectExecute(Sender: TObject);
188 procedure aCutObjectExecute(Sender: TObject);
189 procedure aEditorOptionsExecute(Sender: TObject);
190 procedure aExitExecute(Sender: TObject);
191 procedure aMapOptionsExecute(Sender: TObject);
192 procedure aNewMapExecute(Sender: TObject);
193 procedure aOpenMapExecute(Sender: TObject);
194 procedure aOptimizeExecute(Sender: TObject);
195 procedure aPasteObjectExecute(Sender: TObject);
196 procedure aSelectAllExecute(Sender: TObject);
197 procedure aSaveMapExecute(Sender: TObject);
198 procedure aSaveMapAsExecute(Sender: TObject);
199 procedure aUndoExecute(Sender: TObject);
200 procedure aDeleteMap(Sender: TObject);
201 procedure bApplyPropertyClick(Sender: TObject);
202 procedure bbAddTextureClick(Sender: TObject);
203 procedure bbRemoveTextureClick(Sender: TObject);
204 procedure FormActivate(Sender: TObject);
205 procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
206 procedure FormCreate(Sender: TObject);
207 procedure FormDestroy(Sender: TObject);
208 procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
209 procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
210 procedure FormResize(Sender: TObject);
211 procedure lbTextureListClick(Sender: TObject);
212 procedure lbTextureListDrawItem(Control: TWinControl; Index: Integer;
213 ARect: TRect; State: TOwnerDrawState);
214 procedure miReopenMapClick(Sender: TObject);
215 procedure RenderPanelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
216 procedure RenderPanelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
217 procedure RenderPanelMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
218 procedure RenderPanelPaint(Sender: TObject);
219 procedure RenderPanelResize(Sender: TObject);
220 procedure Splitter1Moved(Sender: TObject);
221 procedure vleObjectPropertyEditButtonClick(Sender: TObject);
222 procedure vleObjectPropertyApply(Sender: TObject);
223 procedure vleObjectPropertyGetPickList(Sender: TObject; const KeyName: String; Values: TStrings);
224 procedure vleObjectPropertyKeyDown(Sender: TObject; var Key: Word;
225 Shift: TShiftState);
226 procedure tbGridOnClick(Sender: TObject);
227 procedure miMapPreviewClick(Sender: TObject);
228 procedure miLayer1Click(Sender: TObject);
229 procedure miLayer2Click(Sender: TObject);
230 procedure miLayer3Click(Sender: TObject);
231 procedure miLayer4Click(Sender: TObject);
232 procedure miLayer5Click(Sender: TObject);
233 procedure miLayer6Click(Sender: TObject);
234 procedure miLayer7Click(Sender: TObject);
235 procedure miLayer8Click(Sender: TObject);
236 procedure miLayer9Click(Sender: TObject);
237 procedure tbShowClick(Sender: TObject);
238 procedure miSnapToGridClick(Sender: TObject);
239 procedure miMiniMapClick(Sender: TObject);
240 procedure miSwitchGridClick(Sender: TObject);
241 procedure miShowEdgesClick(Sender: TObject);
242 procedure minexttabClick(Sender: TObject);
243 procedure miSaveMiniMapClick(Sender: TObject);
244 procedure bClearTextureClick(Sender: TObject);
245 procedure miPackMapClick(Sender: TObject);
246 procedure aRecentFileExecute(Sender: TObject);
247 procedure miMapTestSettingsClick(Sender: TObject);
248 procedure miTestMapClick(Sender: TObject);
249 procedure sbVerticalScroll(Sender: TObject; ScrollCode: TScrollCode;
250 var ScrollPos: Integer);
251 procedure sbHorizontalScroll(Sender: TObject; ScrollCode: TScrollCode;
252 var ScrollPos: Integer);
253 procedure miOpenWadMapClick(Sender: TObject);
254 procedure selectall1Click(Sender: TObject);
255 procedure Splitter1CanResize(Sender: TObject; var NewSize: Integer;
256 var Accept: Boolean);
257 procedure Splitter2CanResize(Sender: TObject; var NewSize: Integer;
258 var Accept: Boolean);
259 procedure vleObjectPropertyEnter(Sender: TObject);
260 procedure vleObjectPropertyExit(Sender: TObject);
261 procedure FormKeyUp(Sender: TObject; var Key: Word;
262 Shift: TShiftState);
263 private
264 procedure Draw();
265 procedure OnIdle(Sender: TObject; var Done: Boolean);
266 public
267 procedure RefreshRecentMenu();
268 procedure OpenMapFile(FileName: String);
269 function RenderMousePos(): TPoint;
270 procedure RecountSelectedObjects();
271 end;
273 const
274 LAYER_BACK = 0;
275 LAYER_WALLS = 1;
276 LAYER_FOREGROUND = 2;
277 LAYER_STEPS = 3;
278 LAYER_WATER = 4;
279 LAYER_ITEMS = 5;
280 LAYER_MONSTERS = 6;
281 LAYER_AREAS = 7;
282 LAYER_TRIGGERS = 8;
284 TEST_MAP_NAME = '$$$_TEST_$$$';
285 LANGUAGE_FILE_NAME = '_Editor.txt';
287 var
288 MainForm: TMainForm;
289 EditorDir: String;
290 OpenedMap: String;
291 OpenedWAD: String;
293 DotColor: TColor;
294 DotEnable: Boolean;
295 DotStep: Word;
296 DotStepOne, DotStepTwo: Word;
297 DotSize: Byte;
298 DrawTexturePanel: Boolean;
299 DrawPanelSize: Boolean;
300 BackColor: TColor;
301 PreviewColor: TColor;
302 UseCheckerboard: Boolean;
303 Scale: Byte;
304 RecentCount: Integer;
305 RecentFiles: TStringList;
306 slInvalidTextures: TStringList;
308 TestGameMode: String;
309 TestLimTime: String;
310 TestLimScore: String;
311 TestOptionsTwoPlayers: Boolean;
312 TestOptionsTeamDamage: Boolean;
313 TestOptionsAllowExit: Boolean;
314 TestOptionsWeaponStay: Boolean;
315 TestOptionsMonstersDM: Boolean;
316 TestD2dExe, TestD2DArgs: String;
317 TestMapOnce: Boolean;
319 LayerEnabled: Array [LAYER_BACK..LAYER_TRIGGERS] of Boolean =
320 (True, True, True, True, True, True, True, True, True);
321 ContourEnabled: Array [LAYER_BACK..LAYER_TRIGGERS] of Boolean =
322 (False, False, False, False, False, False, False, False, False);
323 PreviewMode: Byte = 0;
324 gLanguage: String;
326 FormCaption: String;
329 procedure OpenMap(FileName: String; mapN: String);
330 function AddTexture(aWAD, aSection, aTex: String; silent: Boolean): Boolean;
331 procedure RemoveSelectFromObjects();
332 procedure ChangeShownProperty(Name: String; NewValue: String);
334 implementation
336 uses
337 f_options, e_graphics, e_log, GL, Math,
338 f_mapoptions, g_basic, f_about, f_mapoptimization,
339 f_mapcheck, f_addresource_texture, g_textures,
340 f_activationtype, f_keys, wadreader, fileutil,
341 MAPREADER, f_selectmap, f_savemap, WADEDITOR, MAPDEF,
342 g_map, f_saveminimap, f_addresource, CONFIG, f_packmap,
343 f_addresource_sound, f_maptest, f_choosetype,
344 g_language, f_selectlang, ClipBrd, g_resources;
346 const
347 UNDO_DELETE_PANEL = 1;
348 UNDO_DELETE_ITEM = 2;
349 UNDO_DELETE_AREA = 3;
350 UNDO_DELETE_MONSTER = 4;
351 UNDO_DELETE_TRIGGER = 5;
352 UNDO_ADD_PANEL = 6;
353 UNDO_ADD_ITEM = 7;
354 UNDO_ADD_AREA = 8;
355 UNDO_ADD_MONSTER = 9;
356 UNDO_ADD_TRIGGER = 10;
357 UNDO_MOVE_PANEL = 11;
358 UNDO_MOVE_ITEM = 12;
359 UNDO_MOVE_AREA = 13;
360 UNDO_MOVE_MONSTER = 14;
361 UNDO_MOVE_TRIGGER = 15;
362 UNDO_RESIZE_PANEL = 16;
363 UNDO_RESIZE_TRIGGER = 17;
365 MOUSEACTION_NONE = 0;
366 MOUSEACTION_DRAWPANEL = 1;
367 MOUSEACTION_DRAWTRIGGER = 2;
368 MOUSEACTION_MOVEOBJ = 3;
369 MOUSEACTION_RESIZE = 4;
370 MOUSEACTION_MOVEMAP = 5;
371 MOUSEACTION_DRAWPRESS = 6;
372 MOUSEACTION_NOACTION = 7;
374 RESIZETYPE_NONE = 0;
375 RESIZETYPE_VERTICAL = 1;
376 RESIZETYPE_HORIZONTAL = 2;
378 RESIZEDIR_NONE = 0;
379 RESIZEDIR_DOWN = 1;
380 RESIZEDIR_UP = 2;
381 RESIZEDIR_RIGHT = 3;
382 RESIZEDIR_LEFT = 4;
384 SELECTFLAG_NONE = 0;
385 SELECTFLAG_TELEPORT = 1;
386 SELECTFLAG_DOOR = 2;
387 SELECTFLAG_TEXTURE = 3;
388 SELECTFLAG_LIFT = 4;
389 SELECTFLAG_MONSTER = 5;
390 SELECTFLAG_SPAWNPOINT = 6;
391 SELECTFLAG_SHOTPANEL = 7;
392 SELECTFLAG_SELECTED = 8;
394 RECENT_FILES_MENU_START = 12;
396 CLIPBOARD_SIG = 'DF:ED';
398 type
399 TUndoRec = record
400 UndoType: Byte;
401 case Byte of
402 UNDO_DELETE_PANEL: (Panel: ^TPanel);
403 UNDO_DELETE_ITEM: (Item: TItem);
404 UNDO_DELETE_AREA: (Area: TArea);
405 UNDO_DELETE_MONSTER: (Monster: TMonster);
406 UNDO_DELETE_TRIGGER: (Trigger: TTrigger);
407 UNDO_ADD_PANEL,
408 UNDO_ADD_ITEM,
409 UNDO_ADD_AREA,
410 UNDO_ADD_MONSTER,
411 UNDO_ADD_TRIGGER: (AddID: DWORD);
412 UNDO_MOVE_PANEL,
413 UNDO_MOVE_ITEM,
414 UNDO_MOVE_AREA,
415 UNDO_MOVE_MONSTER,
416 UNDO_MOVE_TRIGGER: (MoveID: DWORD; dX, dY: Integer);
417 UNDO_RESIZE_PANEL,
418 UNDO_RESIZE_TRIGGER: (ResizeID: DWORD; dW, dH: Integer);
419 end;
421 TCopyRec = record
422 ObjectType: Byte;
423 ID: Cardinal;
424 case Byte of
425 OBJECT_PANEL: (Panel: ^TPanel);
426 OBJECT_ITEM: (Item: TItem);
427 OBJECT_AREA: (Area: TArea);
428 OBJECT_MONSTER: (Monster: TMonster);
429 OBJECT_TRIGGER: (Trigger: TTrigger);
430 end;
432 TCopyRecArray = Array of TCopyRec;
434 var
435 gEditorFont: DWORD;
436 gDataLoaded: Boolean = False;
437 ShowMap: Boolean = False;
438 DrawRect: PRect = nil;
439 SnapToGrid: Boolean = True;
441 MousePos: Types.TPoint;
442 LastMovePoint: Types.TPoint;
443 MouseLDown: Boolean;
444 MouseRDown: Boolean;
445 MouseMDown: Boolean;
446 MouseLDownPos: Types.TPoint;
447 MouseRDownPos: Types.TPoint;
448 MouseMDownPos: Types.TPoint;
450 SelectFlag: Byte = SELECTFLAG_NONE;
451 MouseAction: Byte = MOUSEACTION_NONE;
452 ResizeType: Byte = RESIZETYPE_NONE;
453 ResizeDirection: Byte = RESIZEDIR_NONE;
455 DrawPressRect: Boolean = False;
456 EditingProperties: Boolean = False;
458 UndoBuffer: Array of Array of TUndoRec = nil;
461 {$R *.lfm}
463 //----------------------------------------
464 //Далее идут вспомогательные процедуры
465 //----------------------------------------
467 function NameToBool(Name: String): Boolean;
468 begin
469 if Name = BoolNames[True] then
470 Result := True
471 else
472 Result := False;
473 end;
475 function NameToDir(Name: String): TDirection;
476 begin
477 if Name = DirNames[D_LEFT] then
478 Result := D_LEFT
479 else
480 Result := D_RIGHT;
481 end;
483 function NameToDirAdv(Name: String): Byte;
484 begin
485 if Name = DirNamesAdv[1] then
486 Result := 1
487 else
488 if Name = DirNamesAdv[2] then
489 Result := 2
490 else
491 if Name = DirNamesAdv[3] then
492 Result := 3
493 else
494 Result := 0;
495 end;
497 function ActivateToStr(ActivateType: Byte): String;
498 begin
499 Result := '';
501 if ByteBool(ACTIVATE_PLAYERCOLLIDE and ActivateType) then
502 Result := Result + '+PC';
503 if ByteBool(ACTIVATE_MONSTERCOLLIDE and ActivateType) then
504 Result := Result + '+MC';
505 if ByteBool(ACTIVATE_PLAYERPRESS and ActivateType) then
506 Result := Result + '+PP';
507 if ByteBool(ACTIVATE_MONSTERPRESS and ActivateType) then
508 Result := Result + '+MP';
509 if ByteBool(ACTIVATE_SHOT and ActivateType) then
510 Result := Result + '+SH';
511 if ByteBool(ACTIVATE_NOMONSTER and ActivateType) then
512 Result := Result + '+NM';
514 if (Result <> '') and (Result[1] = '+') then
515 Delete(Result, 1, 1);
516 end;
518 function StrToActivate(Str: String): Byte;
519 begin
520 Result := 0;
522 if Pos('PC', Str) > 0 then
523 Result := ACTIVATE_PLAYERCOLLIDE;
524 if Pos('MC', Str) > 0 then
525 Result := Result or ACTIVATE_MONSTERCOLLIDE;
526 if Pos('PP', Str) > 0 then
527 Result := Result or ACTIVATE_PLAYERPRESS;
528 if Pos('MP', Str) > 0 then
529 Result := Result or ACTIVATE_MONSTERPRESS;
530 if Pos('SH', Str) > 0 then
531 Result := Result or ACTIVATE_SHOT;
532 if Pos('NM', Str) > 0 then
533 Result := Result or ACTIVATE_NOMONSTER;
534 end;
536 function KeyToStr(Key: Byte): String;
537 begin
538 Result := '';
540 if ByteBool(KEY_RED and Key) then
541 Result := Result + '+RK';
542 if ByteBool(KEY_GREEN and Key) then
543 Result := Result + '+GK';
544 if ByteBool(KEY_BLUE and Key) then
545 Result := Result + '+BK';
546 if ByteBool(KEY_REDTEAM and Key) then
547 Result := Result + '+RT';
548 if ByteBool(KEY_BLUETEAM and Key) then
549 Result := Result + '+BT';
551 if (Result <> '') and (Result[1] = '+') then
552 Delete(Result, 1, 1);
553 end;
555 function StrToKey(Str: String): Byte;
556 begin
557 Result := 0;
559 if Pos('RK', Str) > 0 then
560 Result := KEY_RED;
561 if Pos('GK', Str) > 0 then
562 Result := Result or KEY_GREEN;
563 if Pos('BK', Str) > 0 then
564 Result := Result or KEY_BLUE;
565 if Pos('RT', Str) > 0 then
566 Result := Result or KEY_REDTEAM;
567 if Pos('BT', Str) > 0 then
568 Result := Result or KEY_BLUETEAM;
569 end;
571 function EffectToStr(Effect: Byte): String;
572 begin
573 if Effect in [EFFECT_TELEPORT..EFFECT_FIRE] then
574 Result := EffectNames[Effect]
575 else
576 Result := EffectNames[EFFECT_NONE];
577 end;
579 function StrToEffect(Str: String): Byte;
580 var
581 i: Integer;
582 begin
583 Result := EFFECT_NONE;
584 for i := EFFECT_TELEPORT to EFFECT_FIRE do
585 if EffectNames[i] = Str then
586 begin
587 Result := i;
588 Exit;
589 end;
590 end;
592 function MonsterToStr(MonType: Byte): String;
593 begin
594 if MonType in [MONSTER_DEMON..MONSTER_MAN] then
595 Result := MonsterNames[MonType]
596 else
597 Result := MonsterNames[MONSTER_ZOMBY];
598 end;
600 function StrToMonster(Str: String): Byte;
601 var
602 i: Integer;
603 begin
604 Result := MONSTER_ZOMBY;
605 for i := MONSTER_DEMON to MONSTER_MAN do
606 if MonsterNames[i] = Str then
607 begin
608 Result := i;
609 Exit;
610 end;
611 end;
613 function ItemToStr(ItemType: Byte): String;
614 begin
615 if ItemType in [ITEM_MEDKIT_SMALL..ITEM_MAX] then
616 Result := ItemNames[ItemType]
617 else
618 Result := ItemNames[ITEM_AMMO_BULLETS];
619 end;
621 function StrToItem(Str: String): Byte;
622 var
623 i: Integer;
624 begin
625 Result := ITEM_AMMO_BULLETS;
626 for i := ITEM_MEDKIT_SMALL to ITEM_MAX do
627 if ItemNames[i] = Str then
628 begin
629 Result := i;
630 Exit;
631 end;
632 end;
634 function ShotToStr(ShotType: Byte): String;
635 begin
636 if ShotType in [TRIGGER_SHOT_PISTOL..TRIGGER_SHOT_MAX] then
637 Result := ShotNames[ShotType]
638 else
639 Result := ShotNames[TRIGGER_SHOT_PISTOL];
640 end;
642 function StrToShot(Str: String): Byte;
643 var
644 i: Integer;
645 begin
646 Result := TRIGGER_SHOT_PISTOL;
647 for i := TRIGGER_SHOT_PISTOL to TRIGGER_SHOT_MAX do
648 if ShotNames[i] = Str then
649 begin
650 Result := i;
651 Exit;
652 end;
653 end;
655 function SelectedObjectCount(): Word;
656 var
657 a: Integer;
658 begin
659 Result := 0;
661 if SelectedObjects = nil then
662 Exit;
664 for a := 0 to High(SelectedObjects) do
665 if SelectedObjects[a].Live then
666 Result := Result + 1;
667 end;
669 function GetFirstSelected(): Integer;
670 var
671 a: Integer;
672 begin
673 Result := -1;
675 if SelectedObjects = nil then
676 Exit;
678 for a := 0 to High(SelectedObjects) do
679 if SelectedObjects[a].Live then
680 begin
681 Result := a;
682 Exit;
683 end;
684 end;
686 function Normalize16(x: Integer): Integer;
687 begin
688 Result := (x div 16) * 16;
689 end;
691 procedure MoveMap(X, Y: Integer);
692 var
693 rx, ry, ScaleSz: Integer;
694 begin
695 with MainForm.RenderPanel do
696 begin
697 ScaleSz := 16 div Scale;
698 // Размер видимой части карты:
699 rx := Min(Normalize16(Width), Normalize16(gMapInfo.Width)) div 2;
700 ry := Min(Normalize16(Height), Normalize16(gMapInfo.Height)) div 2;
701 // Место клика на мини-карте:
702 MapOffset.X := X - (Width - Max(gMapInfo.Width div ScaleSz, 1) - 1);
703 MapOffset.Y := Y - 1;
704 // Это же место на "большой" карте:
705 MapOffset.X := MapOffset.X * ScaleSz;
706 MapOffset.Y := MapOffset.Y * ScaleSz;
707 // Левый верхний угол новой видимой части карты:
708 MapOffset.X := MapOffset.X - rx;
709 MapOffset.Y := MapOffset.Y - ry;
710 // Выход за границы:
711 MapOffset.X := EnsureRange(MapOffset.X, MainForm.sbHorizontal.Min, MainForm.sbHorizontal.Max);
712 MapOffset.Y := EnsureRange(MapOffset.Y, MainForm.sbVertical.Min, MainForm.sbVertical.Max);
713 // Кратно 16:
714 // MapOffset.X := Normalize16(MapOffset.X);
715 // MapOffset.Y := Normalize16(MapOffset.Y);
716 end;
718 MainForm.sbHorizontal.Position := MapOffset.X;
719 MainForm.sbVertical.Position := MapOffset.Y;
721 MapOffset.X := -MapOffset.X;
722 MapOffset.Y := -MapOffset.Y;
724 MainForm.Resize();
725 end;
727 function IsTexturedPanel(PanelType: Word): Boolean;
728 begin
729 Result := WordBool(PanelType and (PANEL_WALL or PANEL_BACK or PANEL_FORE or
730 PANEL_STEP or PANEL_OPENDOOR or PANEL_CLOSEDOOR or
731 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2));
732 end;
734 procedure FillProperty();
735 var
736 _id: DWORD;
737 str: String;
738 begin
739 MainForm.vleObjectProperty.Strings.Clear();
740 MainForm.RecountSelectedObjects();
742 // Отображаем свойства если выделен только один объект:
743 if SelectedObjectCount() <> 1 then
744 Exit;
746 _id := GetFirstSelected();
747 if not SelectedObjects[_id].Live then
748 Exit;
750 with MainForm.vleObjectProperty do
751 with ItemProps[InsertRow(_lc[I_PROP_ID], IntToStr(SelectedObjects[_id].ID), True)] do
752 begin
753 EditStyle := esSimple;
754 ReadOnly := True;
755 end;
757 case SelectedObjects[0].ObjectType of
758 OBJECT_PANEL:
759 begin
760 with MainForm.vleObjectProperty,
761 gPanels[SelectedObjects[_id].ID] do
762 begin
763 with ItemProps[InsertRow(_lc[I_PROP_X], IntToStr(X), True)] do
764 begin
765 EditStyle := esSimple;
766 MaxLength := 5;
767 end;
769 with ItemProps[InsertRow(_lc[I_PROP_Y], IntToStr(Y), True)] do
770 begin
771 EditStyle := esSimple;
772 MaxLength := 5;
773 end;
775 with ItemProps[InsertRow(_lc[I_PROP_WIDTH], IntToStr(Width), True)] do
776 begin
777 EditStyle := esSimple;
778 MaxLength := 5;
779 end;
781 with ItemProps[InsertRow(_lc[I_PROP_HEIGHT], IntToStr(Height), True)] do
782 begin
783 EditStyle := esSimple;
784 MaxLength := 5;
785 end;
787 with ItemProps[InsertRow(_lc[I_PROP_PANEL_TYPE], GetPanelName(PanelType), True)] do
788 begin
789 EditStyle := esEllipsis;
790 ReadOnly := True;
791 end;
793 if IsTexturedPanel(PanelType) then
794 begin // Может быть текстура
795 with ItemProps[InsertRow(_lc[I_PROP_PANEL_TEX], TextureName, True)] do
796 begin
797 EditStyle := esEllipsis;
798 ReadOnly := True;
799 end;
801 if TextureName <> '' then
802 begin // Есть текстура
803 with ItemProps[InsertRow(_lc[I_PROP_PANEL_ALPHA], IntToStr(Alpha), True)] do
804 begin
805 EditStyle := esSimple;
806 MaxLength := 3;
807 end;
809 with ItemProps[InsertRow(_lc[I_PROP_PANEL_BLEND], BoolNames[Blending], True)] do
810 begin
811 EditStyle := esPickList;
812 ReadOnly := True;
813 end;
814 end;
815 end;
816 end;
817 end;
819 OBJECT_ITEM:
820 begin
821 with MainForm.vleObjectProperty,
822 gItems[SelectedObjects[_id].ID] do
823 begin
824 with ItemProps[InsertRow(_lc[I_PROP_X], IntToStr(X), True)] do
825 begin
826 EditStyle := esSimple;
827 MaxLength := 5;
828 end;
830 with ItemProps[InsertRow(_lc[I_PROP_Y], IntToStr(Y), True)] do
831 begin
832 EditStyle := esSimple;
833 MaxLength := 5;
834 end;
836 with ItemProps[InsertRow(_lc[I_PROP_DM_ONLY], BoolNames[OnlyDM], True)] do
837 begin
838 EditStyle := esPickList;
839 ReadOnly := True;
840 end;
842 with ItemProps[InsertRow(_lc[I_PROP_ITEM_FALLS], BoolNames[Fall], True)] do
843 begin
844 EditStyle := esPickList;
845 ReadOnly := True;
846 end;
847 end;
848 end;
850 OBJECT_MONSTER:
851 begin
852 with MainForm.vleObjectProperty,
853 gMonsters[SelectedObjects[_id].ID] do
854 begin
855 with ItemProps[InsertRow(_lc[I_PROP_X], IntToStr(X), True)] do
856 begin
857 EditStyle := esSimple;
858 MaxLength := 5;
859 end;
861 with ItemProps[InsertRow(_lc[I_PROP_Y], IntToStr(Y), True)] do
862 begin
863 EditStyle := esSimple;
864 MaxLength := 5;
865 end;
867 with ItemProps[InsertRow(_lc[I_PROP_DIRECTION], DirNames[Direction], True)] do
868 begin
869 EditStyle := esPickList;
870 ReadOnly := True;
871 end;
872 end;
873 end;
875 OBJECT_AREA:
876 begin
877 with MainForm.vleObjectProperty,
878 gAreas[SelectedObjects[_id].ID] do
879 begin
880 with ItemProps[InsertRow(_lc[I_PROP_X], IntToStr(X), True)] do
881 begin
882 EditStyle := esSimple;
883 MaxLength := 5;
884 end;
886 with ItemProps[InsertRow(_lc[I_PROP_Y], IntToStr(Y), True)] do
887 begin
888 EditStyle := esSimple;
889 MaxLength := 5;
890 end;
892 with ItemProps[InsertRow(_lc[I_PROP_DIRECTION], DirNames[Direction], True)] do
893 begin
894 EditStyle := esPickList;
895 ReadOnly := True;
896 end;
897 end;
898 end;
900 OBJECT_TRIGGER:
901 begin
902 with MainForm.vleObjectProperty,
903 gTriggers[SelectedObjects[_id].ID] do
904 begin
905 with ItemProps[InsertRow(_lc[I_PROP_TR_TYPE], GetTriggerName(TriggerType), True)] do
906 begin
907 EditStyle := esSimple;
908 ReadOnly := True;
909 end;
911 with ItemProps[InsertRow(_lc[I_PROP_X], IntToStr(X), True)] do
912 begin
913 EditStyle := esSimple;
914 MaxLength := 5;
915 end;
917 with ItemProps[InsertRow(_lc[I_PROP_Y], IntToStr(Y), True)] do
918 begin
919 EditStyle := esSimple;
920 MaxLength := 5;
921 end;
923 with ItemProps[InsertRow(_lc[I_PROP_WIDTH], IntToStr(Width), True)] do
924 begin
925 EditStyle := esSimple;
926 MaxLength := 5;
927 end;
929 with ItemProps[InsertRow(_lc[I_PROP_HEIGHT], IntToStr(Height), True)] do
930 begin
931 EditStyle := esSimple;
932 MaxLength := 5;
933 end;
935 with ItemProps[InsertRow(_lc[I_PROP_TR_ENABLED], BoolNames[Enabled], True)] do
936 begin
937 EditStyle := esPickList;
938 ReadOnly := True;
939 end;
941 with ItemProps[InsertRow(_lc[I_PROP_TR_TEXTURE_PANEL], IntToStr(TexturePanel), True)] do
942 begin
943 EditStyle := esEllipsis;
944 ReadOnly := True;
945 end;
947 with ItemProps[InsertRow(_lc[I_PROP_TR_ACTIVATION], ActivateToStr(ActivateType), True)] do
948 begin
949 EditStyle := esEllipsis;
950 ReadOnly := True;
951 end;
953 with ItemProps[InsertRow(_lc[I_PROP_TR_KEYS], KeyToStr(Key), True)] do
954 begin
955 EditStyle := esEllipsis;
956 ReadOnly := True;
957 end;
959 case TriggerType of
960 TRIGGER_EXIT:
961 begin
962 str := win2utf(Data.MapName);
963 with ItemProps[InsertRow(_lc[I_PROP_TR_NEXT_MAP], str, True)] do
964 begin
965 EditStyle := esEllipsis;
966 ReadOnly := True;
967 end;
968 end;
970 TRIGGER_TELEPORT:
971 begin
972 with ItemProps[InsertRow(_lc[I_PROP_TR_TELEPORT_TO], Format('(%d:%d)', [Data.TargetPoint.X, Data.TargetPoint.Y]), True)] do
973 begin
974 EditStyle := esEllipsis;
975 ReadOnly := True;
976 end;
978 with ItemProps[InsertRow(_lc[I_PROP_TR_D2D], BoolNames[Data.d2d_teleport], True)] do
979 begin
980 EditStyle := esPickList;
981 ReadOnly := True;
982 end;
984 with ItemProps[InsertRow(_lc[I_PROP_TR_TELEPORT_SILENT], BoolNames[Data.silent_teleport], True)] do
985 begin
986 EditStyle := esPickList;
987 ReadOnly := True;
988 end;
990 with ItemProps[InsertRow(_lc[I_PROP_TR_TELEPORT_DIR], DirNamesAdv[Data.TlpDir], True)] do
991 begin
992 EditStyle := esPickList;
993 ReadOnly := True;
994 end;
995 end;
997 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR,
998 TRIGGER_DOOR, TRIGGER_DOOR5:
999 begin
1000 with ItemProps[InsertRow(_lc[I_PROP_TR_DOOR_PANEL], IntToStr(Data.PanelID), True)] do
1001 begin
1002 EditStyle := esEllipsis;
1003 ReadOnly := True;
1004 end;
1006 with ItemProps[InsertRow(_lc[I_PROP_TR_SILENT], BoolNames[Data.NoSound], True)] do
1007 begin
1008 EditStyle := esPickList;
1009 ReadOnly := True;
1010 end;
1012 with ItemProps[InsertRow(_lc[I_PROP_TR_D2D], BoolNames[Data.d2d_doors], True)] do
1013 begin
1014 EditStyle := esPickList;
1015 ReadOnly := True;
1016 end;
1017 end;
1019 TRIGGER_CLOSETRAP, TRIGGER_TRAP:
1020 begin
1021 with ItemProps[InsertRow(_lc[I_PROP_TR_TRAP_PANEL], IntToStr(Data.PanelID), True)] do
1022 begin
1023 EditStyle := esEllipsis;
1024 ReadOnly := True;
1025 end;
1027 with ItemProps[InsertRow(_lc[I_PROP_TR_SILENT], BoolNames[Data.NoSound], True)] do
1028 begin
1029 EditStyle := esPickList;
1030 ReadOnly := True;
1031 end;
1033 with ItemProps[InsertRow(_lc[I_PROP_TR_D2D], BoolNames[Data.d2d_doors], True)] do
1034 begin
1035 EditStyle := esPickList;
1036 ReadOnly := True;
1037 end;
1038 end;
1040 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF,
1041 TRIGGER_ONOFF:
1042 begin
1043 with ItemProps[InsertRow(_lc[I_PROP_TR_EX_AREA],
1044 Format('(%d:%d %d:%d)', [Data.tX, Data.tY, Data.tWidth, Data.tHeight]), True)] do
1045 begin
1046 EditStyle := esEllipsis;
1047 ReadOnly := True;
1048 end;
1050 with ItemProps[InsertRow(_lc[I_PROP_TR_EX_DELAY], IntToStr(Data.Wait), True)] do
1051 begin
1052 EditStyle := esSimple;
1053 MaxLength := 5;
1054 end;
1056 with ItemProps[InsertRow(_lc[I_PROP_TR_EX_COUNT], IntToStr(Data.Count), True)] do
1057 begin
1058 EditStyle := esSimple;
1059 MaxLength := 5;
1060 end;
1062 with ItemProps[InsertRow(_lc[I_PROP_TR_EX_MONSTER], IntToStr(Data.MonsterID-1), True)] do
1063 begin
1064 EditStyle := esEllipsis;
1065 ReadOnly := True;
1066 end;
1068 if TriggerType = TRIGGER_PRESS then
1069 with ItemProps[InsertRow(_lc[I_PROP_TR_EX_RANDOM], BoolNames[Data.ExtRandom], True)] do
1070 begin
1071 EditStyle := esPickList;
1072 ReadOnly := True;
1073 end;
1074 end;
1076 TRIGGER_SECRET:
1079 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
1080 begin
1081 with ItemProps[InsertRow(_lc[I_PROP_TR_LIFT_PANEL], IntToStr(Data.PanelID), True)] do
1082 begin
1083 EditStyle := esEllipsis;
1084 ReadOnly := True;
1085 end;
1087 with ItemProps[InsertRow(_lc[I_PROP_TR_SILENT], BoolNames[Data.NoSound], True)] do
1088 begin
1089 EditStyle := esPickList;
1090 ReadOnly := True;
1091 end;
1093 with ItemProps[InsertRow(_lc[I_PROP_TR_D2D], BoolNames[Data.d2d_doors], True)] do
1094 begin
1095 EditStyle := esPickList;
1096 ReadOnly := True;
1097 end;
1098 end;
1100 TRIGGER_TEXTURE:
1101 begin
1102 with ItemProps[InsertRow(_lc[I_PROP_TR_TEXTURE_ONCE], BoolNames[Data.ActivateOnce], True)] do
1103 begin
1104 EditStyle := esPickList;
1105 ReadOnly := True;
1106 end;
1108 with ItemProps[InsertRow(_lc[I_PROP_TR_TEXTURE_ANIM_ONCE], BoolNames[Data.AnimOnce], True)] do
1109 begin
1110 EditStyle := esPickList;
1111 ReadOnly := True;
1112 end;
1113 end;
1115 TRIGGER_SOUND:
1116 begin
1117 str := win2utf(Data.SoundName);
1118 with ItemProps[InsertRow(_lc[I_PROP_TR_SOUND_NAME], str, True)] do
1119 begin
1120 EditStyle := esEllipsis;
1121 ReadOnly := True;
1122 end;
1124 with ItemProps[InsertRow(_lc[I_PROP_TR_SOUND_VOLUME], IntToStr(Data.Volume), True)] do
1125 begin
1126 EditStyle := esSimple;
1127 MaxLength := 3;
1128 end;
1130 with ItemProps[InsertRow(_lc[I_PROP_TR_SOUND_PAN], IntToStr(Data.Pan), True)] do
1131 begin
1132 EditStyle := esSimple;
1133 MaxLength := 3;
1134 end;
1136 with ItemProps[InsertRow(_lc[I_PROP_TR_SOUND_COUNT], IntToStr(Data.PlayCount), True)] do
1137 begin
1138 EditStyle := esSimple;
1139 MaxLength := 3;
1140 end;
1142 with ItemProps[InsertRow(_lc[I_PROP_TR_SOUND_LOCAL], BoolNames[Data.Local], True)] do
1143 begin
1144 EditStyle := esPickList;
1145 ReadOnly := True;
1146 end;
1148 with ItemProps[InsertRow(_lc[I_PROP_TR_SOUND_SWITCH], BoolNames[Data.SoundSwitch], True)] do
1149 begin
1150 EditStyle := esPickList;
1151 ReadOnly := True;
1152 end;
1153 end;
1155 TRIGGER_SPAWNMONSTER:
1156 begin
1157 with ItemProps[InsertRow(_lc[I_PROP_TR_MONSTER_TYPE], MonsterToStr(Data.MonType), True)] do
1158 begin
1159 EditStyle := esEllipsis;
1160 ReadOnly := True;
1161 end;
1163 with ItemProps[InsertRow(_lc[I_PROP_TR_SPAWN_TO],
1164 Format('(%d:%d)', [Data.MonPos.X, Data.MonPos.Y]), True)] do
1165 begin
1166 EditStyle := esEllipsis;
1167 ReadOnly := True;
1168 end;
1170 with ItemProps[InsertRow(_lc[I_PROP_DIRECTION], DirNames[TDirection(Data.MonDir)], True)] do
1171 begin
1172 EditStyle := esPickList;
1173 ReadOnly := True;
1174 end;
1176 with ItemProps[InsertRow(_lc[I_PROP_TR_HEALTH], IntToStr(Data.MonHealth), True)] do
1177 begin
1178 EditStyle := esSimple;
1179 MaxLength := 5;
1180 end;
1182 with ItemProps[InsertRow(_lc[I_PROP_TR_MONSTER_ACTIVE], BoolNames[Data.MonActive], True)] do
1183 begin
1184 EditStyle := esPickList;
1185 ReadOnly := True;
1186 end;
1188 with ItemProps[InsertRow(_lc[I_PROP_TR_COUNT], IntToStr(Data.MonCount), True)] do
1189 begin
1190 EditStyle := esSimple;
1191 MaxLength := 5;
1192 end;
1194 with ItemProps[InsertRow(_lc[I_PROP_TR_FX_TYPE], EffectToStr(Data.MonEffect), True)] do
1195 begin
1196 EditStyle := esEllipsis;
1197 ReadOnly := True;
1198 end;
1200 with ItemProps[InsertRow(_lc[I_PROP_TR_SPAWN_MAX], IntToStr(Data.MonMax), True)] do
1201 begin
1202 EditStyle := esSimple;
1203 MaxLength := 5;
1204 end;
1206 with ItemProps[InsertRow(_lc[I_PROP_TR_SPAWN_DELAY], IntToStr(Data.MonDelay), True)] do
1207 begin
1208 EditStyle := esSimple;
1209 MaxLength := 5;
1210 end;
1212 case Data.MonBehav of
1213 1: str := _lc[I_PROP_TR_MONSTER_BEHAVIOUR_1];
1214 2: str := _lc[I_PROP_TR_MONSTER_BEHAVIOUR_2];
1215 3: str := _lc[I_PROP_TR_MONSTER_BEHAVIOUR_3];
1216 4: str := _lc[I_PROP_TR_MONSTER_BEHAVIOUR_4];
1217 5: str := _lc[I_PROP_TR_MONSTER_BEHAVIOUR_5];
1218 else str := _lc[I_PROP_TR_MONSTER_BEHAVIOUR_0];
1219 end;
1220 with ItemProps[InsertRow(_lc[I_PROP_TR_MONSTER_BEHAVIOUR], str, True)] do
1221 begin
1222 EditStyle := esPickList;
1223 ReadOnly := True;
1224 end;
1225 end;
1227 TRIGGER_SPAWNITEM:
1228 begin
1229 with ItemProps[InsertRow(_lc[I_PROP_TR_ITEM_TYPE], ItemToStr(Data.ItemType), True)] do
1230 begin
1231 EditStyle := esEllipsis;
1232 ReadOnly := True;
1233 end;
1235 with ItemProps[InsertRow(_lc[I_PROP_TR_SPAWN_TO],
1236 Format('(%d:%d)', [Data.ItemPos.X, Data.ItemPos.Y]), True)] do
1237 begin
1238 EditStyle := esEllipsis;
1239 ReadOnly := True;
1240 end;
1242 with ItemProps[InsertRow(_lc[I_PROP_DM_ONLY], BoolNames[Data.ItemOnlyDM], True)] do
1243 begin
1244 EditStyle := esPickList;
1245 ReadOnly := True;
1246 end;
1248 with ItemProps[InsertRow(_lc[I_PROP_ITEM_FALLS], BoolNames[Data.ItemFalls], True)] do
1249 begin
1250 EditStyle := esPickList;
1251 ReadOnly := True;
1252 end;
1254 with ItemProps[InsertRow(_lc[I_PROP_TR_COUNT], IntToStr(Data.ItemCount), True)] do
1255 begin
1256 EditStyle := esSimple;
1257 MaxLength := 5;
1258 end;
1260 with ItemProps[InsertRow(_lc[I_PROP_TR_FX_TYPE], EffectToStr(Data.ItemEffect), True)] do
1261 begin
1262 EditStyle := esEllipsis;
1263 ReadOnly := True;
1264 end;
1266 with ItemProps[InsertRow(_lc[I_PROP_TR_SPAWN_MAX], IntToStr(Data.ItemMax), True)] do
1267 begin
1268 EditStyle := esSimple;
1269 MaxLength := 5;
1270 end;
1272 with ItemProps[InsertRow(_lc[I_PROP_TR_SPAWN_DELAY], IntToStr(Data.ItemDelay), True)] do
1273 begin
1274 EditStyle := esSimple;
1275 MaxLength := 5;
1276 end;
1277 end;
1279 TRIGGER_MUSIC:
1280 begin
1281 str := win2utf(Data.MusicName);
1282 with ItemProps[InsertRow(_lc[I_PROP_TR_MUSIC_NAME], str, True)] do
1283 begin
1284 EditStyle := esEllipsis;
1285 ReadOnly := True;
1286 end;
1288 if Data.MusicAction = 1 then
1289 str := _lc[I_PROP_TR_MUSIC_ON]
1290 else
1291 str := _lc[I_PROP_TR_MUSIC_OFF];
1293 with ItemProps[InsertRow(_lc[I_PROP_TR_MUSIC_ACT], str, True)] do
1294 begin
1295 EditStyle := esPickList;
1296 ReadOnly := True;
1297 end;
1298 end;
1300 TRIGGER_PUSH:
1301 begin
1302 with ItemProps[InsertRow(_lc[I_PROP_TR_PUSH_ANGLE], IntToStr(Data.PushAngle), True)] do
1303 begin
1304 EditStyle := esSimple;
1305 MaxLength := 4;
1306 end;
1307 with ItemProps[InsertRow(_lc[I_PROP_TR_PUSH_FORCE], IntToStr(Data.PushForce), True)] do
1308 begin
1309 EditStyle := esSimple;
1310 MaxLength := 4;
1311 end;
1312 with ItemProps[InsertRow(_lc[I_PROP_TR_PUSH_RESET], BoolNames[Data.ResetVel], True)] do
1313 begin
1314 EditStyle := esPickList;
1315 ReadOnly := True;
1316 end;
1317 end;
1319 TRIGGER_SCORE:
1320 begin
1321 case Data.ScoreAction of
1322 1: str := _lc[I_PROP_TR_SCORE_ACT_1];
1323 2: str := _lc[I_PROP_TR_SCORE_ACT_2];
1324 3: str := _lc[I_PROP_TR_SCORE_ACT_3];
1325 else str := _lc[I_PROP_TR_SCORE_ACT_0];
1326 end;
1327 with ItemProps[InsertRow(_lc[I_PROP_TR_SCORE_ACT], str, True)] do
1328 begin
1329 EditStyle := esPickList;
1330 ReadOnly := True;
1331 end;
1332 with ItemProps[InsertRow(_lc[I_PROP_TR_COUNT], IntToStr(Data.ScoreCount), True)] do
1333 begin
1334 EditStyle := esSimple;
1335 MaxLength := 3;
1336 end;
1337 case Data.ScoreTeam of
1338 1: str := _lc[I_PROP_TR_SCORE_TEAM_1];
1339 2: str := _lc[I_PROP_TR_SCORE_TEAM_2];
1340 3: str := _lc[I_PROP_TR_SCORE_TEAM_3];
1341 else str := _lc[I_PROP_TR_SCORE_TEAM_0];
1342 end;
1343 with ItemProps[InsertRow(_lc[I_PROP_TR_SCORE_TEAM], str, True)] do
1344 begin
1345 EditStyle := esPickList;
1346 ReadOnly := True;
1347 end;
1348 with ItemProps[InsertRow(_lc[I_PROP_TR_SCORE_CON], BoolNames[Data.ScoreCon], True)] do
1349 begin
1350 EditStyle := esPickList;
1351 ReadOnly := True;
1352 end;
1353 with ItemProps[InsertRow(_lc[I_PROP_TR_SCORE_MSG], BoolNames[Data.ScoreMsg], True)] do
1354 begin
1355 EditStyle := esPickList;
1356 ReadOnly := True;
1357 end;
1358 end;
1360 TRIGGER_MESSAGE:
1361 begin
1362 case Data.MessageKind of
1363 1: str := _lc[I_PROP_TR_MESSAGE_KIND_1];
1364 else str := _lc[I_PROP_TR_MESSAGE_KIND_0];
1365 end;
1366 with ItemProps[InsertRow(_lc[I_PROP_TR_MESSAGE_KIND], str, True)] do
1367 begin
1368 EditStyle := esPickList;
1369 ReadOnly := True;
1370 end;
1371 case Data.MessageSendTo of
1372 1: str := _lc[I_PROP_TR_MESSAGE_TO_1];
1373 2: str := _lc[I_PROP_TR_MESSAGE_TO_2];
1374 3: str := _lc[I_PROP_TR_MESSAGE_TO_3];
1375 4: str := _lc[I_PROP_TR_MESSAGE_TO_4];
1376 5: str := _lc[I_PROP_TR_MESSAGE_TO_5];
1377 else str := _lc[I_PROP_TR_MESSAGE_TO_0];
1378 end;
1379 with ItemProps[InsertRow(_lc[I_PROP_TR_MESSAGE_TO], str, True)] do
1380 begin
1381 EditStyle := esPickList;
1382 ReadOnly := True;
1383 end;
1384 str := win2utf(Data.MessageText);
1385 with ItemProps[InsertRow(_lc[I_PROP_TR_MESSAGE_TEXT], str, True)] do
1386 begin
1387 EditStyle := esSimple;
1388 MaxLength := 100;
1389 end;
1390 with ItemProps[InsertRow(_lc[I_PROP_TR_MESSAGE_TIME], IntToStr(Data.MessageTime), True)] do
1391 begin
1392 EditStyle := esSimple;
1393 MaxLength := 5;
1394 end;
1395 end;
1397 TRIGGER_DAMAGE:
1398 begin
1399 with ItemProps[InsertRow(_lc[I_PROP_TR_DAMAGE_VALUE], IntToStr(Data.DamageValue), True)] do
1400 begin
1401 EditStyle := esSimple;
1402 MaxLength := 5;
1403 end;
1404 with ItemProps[InsertRow(_lc[I_PROP_TR_INTERVAL], IntToStr(Data.DamageInterval), True)] do
1405 begin
1406 EditStyle := esSimple;
1407 MaxLength := 5;
1408 end;
1409 case Data.DamageKind of
1410 3: str := _lc[I_PROP_TR_DAMAGE_KIND_3];
1411 4: str := _lc[I_PROP_TR_DAMAGE_KIND_4];
1412 5: str := _lc[I_PROP_TR_DAMAGE_KIND_5];
1413 6: str := _lc[I_PROP_TR_DAMAGE_KIND_6];
1414 7: str := _lc[I_PROP_TR_DAMAGE_KIND_7];
1415 8: str := _lc[I_PROP_TR_DAMAGE_KIND_8];
1416 else str := _lc[I_PROP_TR_DAMAGE_KIND_0];
1417 end;
1418 with ItemProps[InsertRow(_lc[I_PROP_TR_DAMAGE_KIND], str, True)] do
1419 begin
1420 EditStyle := esPickList;
1421 ReadOnly := True;
1422 end;
1423 end;
1425 TRIGGER_HEALTH:
1426 begin
1427 with ItemProps[InsertRow(_lc[I_PROP_TR_HEALTH], IntToStr(Data.HealValue), True)] do
1428 begin
1429 EditStyle := esSimple;
1430 MaxLength := 5;
1431 end;
1432 with ItemProps[InsertRow(_lc[I_PROP_TR_INTERVAL], IntToStr(Data.HealInterval), True)] do
1433 begin
1434 EditStyle := esSimple;
1435 MaxLength := 5;
1436 end;
1437 with ItemProps[InsertRow(_lc[I_PROP_TR_HEALTH_MAX], BoolNames[Data.HealMax], True)] do
1438 begin
1439 EditStyle := esPickList;
1440 ReadOnly := True;
1441 end;
1442 with ItemProps[InsertRow(_lc[I_PROP_TR_SILENT], BoolNames[Data.HealSilent], True)] do
1443 begin
1444 EditStyle := esPickList;
1445 ReadOnly := True;
1446 end;
1447 end;
1449 TRIGGER_SHOT:
1450 begin
1451 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_TYPE], ShotToStr(Data.ShotType), True)] do
1452 begin
1453 EditStyle := esEllipsis;
1454 ReadOnly := True;
1455 end;
1457 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_SOUND], BoolNames[Data.ShotSound], True)] do
1458 begin
1459 EditStyle := esPickList;
1460 ReadOnly := True;
1461 end;
1463 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_PANEL], IntToStr(Data.ShotPanelID), True)] do
1464 begin
1465 EditStyle := esEllipsis;
1466 ReadOnly := True;
1467 end;
1469 case Data.ShotTarget of
1470 1: str := _lc[I_PROP_TR_SHOT_TO_1];
1471 2: str := _lc[I_PROP_TR_SHOT_TO_2];
1472 3: str := _lc[I_PROP_TR_SHOT_TO_3];
1473 4: str := _lc[I_PROP_TR_SHOT_TO_4];
1474 5: str := _lc[I_PROP_TR_SHOT_TO_5];
1475 6: str := _lc[I_PROP_TR_SHOT_TO_6];
1476 else str := _lc[I_PROP_TR_SHOT_TO_0];
1477 end;
1478 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_TO], str, True)] do
1479 begin
1480 EditStyle := esPickList;
1481 ReadOnly := True;
1482 end;
1484 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_SIGHT], IntToStr(Data.ShotIntSight), True)] do
1485 begin
1486 EditStyle := esSimple;
1487 MaxLength := 3;
1488 end;
1490 case Data.ShotAim of
1491 1: str := _lc[I_PROP_TR_SHOT_AIM_1];
1492 2: str := _lc[I_PROP_TR_SHOT_AIM_2];
1493 3: str := _lc[I_PROP_TR_SHOT_AIM_3];
1494 else str := _lc[I_PROP_TR_SHOT_AIM_0];
1495 end;
1496 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_AIM], str, True)] do
1497 begin
1498 EditStyle := esPickList;
1499 ReadOnly := True;
1500 end;
1502 with ItemProps[InsertRow(_lc[I_PROP_TR_SPAWN_TO],
1503 Format('(%d:%d)', [Data.ShotPos.X, Data.ShotPos.Y]), True)] do
1504 begin
1505 EditStyle := esEllipsis;
1506 ReadOnly := True;
1507 end;
1509 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_ANGLE], IntToStr(Data.ShotAngle), True)] do
1510 begin
1511 EditStyle := esSimple;
1512 MaxLength := 4;
1513 end;
1515 with ItemProps[InsertRow(_lc[I_PROP_TR_EX_DELAY], IntToStr(Data.ShotWait), True)] do
1516 begin
1517 EditStyle := esSimple;
1518 MaxLength := 5;
1519 end;
1521 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_ACC], IntToStr(Data.ShotAccuracy), True)] do
1522 begin
1523 EditStyle := esSimple;
1524 MaxLength := 5;
1525 end;
1527 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_AMMO], IntToStr(Data.ShotAmmo), True)] do
1528 begin
1529 EditStyle := esSimple;
1530 MaxLength := 5;
1531 end;
1533 with ItemProps[InsertRow(_lc[I_PROP_TR_SHOT_RELOAD], IntToStr(Data.ShotIntReload), True)] do
1534 begin
1535 EditStyle := esSimple;
1536 MaxLength := 4;
1537 end;
1538 end;
1540 TRIGGER_EFFECT:
1541 begin
1542 with ItemProps[InsertRow(_lc[I_PROP_TR_COUNT], IntToStr(Data.FXCount), True)] do
1543 begin
1544 EditStyle := esSimple;
1545 MaxLength := 3;
1546 end;
1548 if Data.FXType = 0 then
1549 str := _lc[I_PROP_TR_EFFECT_PARTICLE]
1550 else
1551 str := _lc[I_PROP_TR_EFFECT_ANIMATION];
1552 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_TYPE], str, True)] do
1553 begin
1554 EditStyle := esEllipsis;
1555 ReadOnly := True;
1556 end;
1558 str := '';
1559 if Data.FXType = 0 then
1560 case Data.FXSubType of
1561 TRIGGER_EFFECT_SLIQUID:
1562 str := _lc[I_PROP_TR_EFFECT_SLIQUID];
1563 TRIGGER_EFFECT_LLIQUID:
1564 str := _lc[I_PROP_TR_EFFECT_LLIQUID];
1565 TRIGGER_EFFECT_DLIQUID:
1566 str := _lc[I_PROP_TR_EFFECT_DLIQUID];
1567 TRIGGER_EFFECT_BLOOD:
1568 str := _lc[I_PROP_TR_EFFECT_BLOOD];
1569 TRIGGER_EFFECT_SPARK:
1570 str := _lc[I_PROP_TR_EFFECT_SPARK];
1571 TRIGGER_EFFECT_BUBBLE:
1572 str := _lc[I_PROP_TR_EFFECT_BUBBLE];
1573 end;
1574 if Data.FXType = 1 then
1575 begin
1576 if (Data.FXSubType = 0) or (Data.FXSubType > EFFECT_FIRE) then
1577 Data.FXSubType := EFFECT_TELEPORT;
1578 str := EffectToStr(Data.FXSubType);
1579 end;
1580 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_SUBTYPE], str, True)] do
1581 begin
1582 EditStyle := esEllipsis;
1583 ReadOnly := True;
1584 end;
1586 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_COLOR], IntToStr(Data.FXColorR or (Data.FXColorG shl 8) or (Data.FXColorB shl 16)), True)] do
1587 begin
1588 EditStyle := esEllipsis;
1589 ReadOnly := True;
1590 end;
1592 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_CENTER], BoolNames[Data.FXPos = 0], True)] do
1593 begin
1594 EditStyle := esPickList;
1595 ReadOnly := True;
1596 end;
1598 with ItemProps[InsertRow(_lc[I_PROP_TR_EX_DELAY], IntToStr(Data.FXWait), True)] do
1599 begin
1600 EditStyle := esSimple;
1601 MaxLength := 5;
1602 end;
1604 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_VELX], IntToStr(Data.FXVelX), True)] do
1605 begin
1606 EditStyle := esSimple;
1607 MaxLength := 4;
1608 end;
1610 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_VELY], IntToStr(Data.FXVelY), True)] do
1611 begin
1612 EditStyle := esSimple;
1613 MaxLength := 4;
1614 end;
1616 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_SPL], IntToStr(Data.FXSpreadL), True)] do
1617 begin
1618 EditStyle := esSimple;
1619 MaxLength := 3;
1620 end;
1622 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_SPR], IntToStr(Data.FXSpreadR), True)] do
1623 begin
1624 EditStyle := esSimple;
1625 MaxLength := 3;
1626 end;
1628 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_SPU], IntToStr(Data.FXSpreadU), True)] do
1629 begin
1630 EditStyle := esSimple;
1631 MaxLength := 3;
1632 end;
1634 with ItemProps[InsertRow(_lc[I_PROP_TR_EFFECT_SPD], IntToStr(Data.FXSpreadD), True)] do
1635 begin
1636 EditStyle := esSimple;
1637 MaxLength := 3;
1638 end;
1639 end;
1640 end; //case TriggerType
1641 end;
1642 end; // OBJECT_TRIGGER:
1643 end;
1644 end;
1646 procedure ChangeShownProperty(Name: String; NewValue: String);
1647 var
1648 row: Integer;
1649 begin
1650 if SelectedObjectCount() <> 1 then
1651 Exit;
1652 if not SelectedObjects[GetFirstSelected()].Live then
1653 Exit;
1655 // Есть ли такой ключ:
1656 if MainForm.vleObjectProperty.FindRow(Name, row) then
1657 begin
1658 MainForm.vleObjectProperty.Values[Name] := NewValue;
1659 end;
1660 end;
1662 procedure SelectObject(fObjectType: Byte; fID: DWORD; Multi: Boolean);
1663 var
1664 a: Integer;
1665 b: Boolean;
1666 begin
1667 if Multi then
1668 begin
1669 b := False;
1671 // Уже выделен - убираем:
1672 if SelectedObjects <> nil then
1673 for a := 0 to High(SelectedObjects) do
1674 with SelectedObjects[a] do
1675 if Live and (ID = fID) and
1676 (ObjectType = fObjectType) then
1677 begin
1678 Live := False;
1679 b := True;
1680 end;
1682 if b then
1683 Exit;
1685 SetLength(SelectedObjects, Length(SelectedObjects)+1);
1687 with SelectedObjects[High(SelectedObjects)] do
1688 begin
1689 ObjectType := fObjectType;
1690 ID := fID;
1691 Live := True;
1692 end;
1693 end
1694 else // not Multi
1695 begin
1696 SetLength(SelectedObjects, 1);
1698 with SelectedObjects[0] do
1699 begin
1700 ObjectType := fObjectType;
1701 ID := fID;
1702 Live := True;
1703 end;
1704 end;
1706 MainForm.miCopy.Enabled := True;
1707 MainForm.miCut.Enabled := True;
1709 if fObjectType = OBJECT_PANEL then
1710 begin
1711 MainForm.miToFore.Enabled := True;
1712 MainForm.miToBack.Enabled := True;
1713 end;
1714 end;
1716 procedure RemoveSelectFromObjects();
1717 begin
1718 SelectedObjects := nil;
1719 DrawPressRect := False;
1720 MouseLDown := False;
1721 MouseRDown := False;
1722 MouseAction := MOUSEACTION_NONE;
1723 SelectFlag := SELECTFLAG_NONE;
1724 ResizeType := RESIZETYPE_NONE;
1725 ResizeDirection := RESIZEDIR_NONE;
1727 MainForm.vleObjectProperty.Strings.Clear();
1729 MainForm.miCopy.Enabled := False;
1730 MainForm.miCut.Enabled := False;
1731 MainForm.miToFore.Enabled := False;
1732 MainForm.miToBack.Enabled := False;
1733 end;
1735 procedure DeleteSelectedObjects();
1736 var
1737 i, a, ii: Integer;
1738 b: Boolean;
1739 begin
1740 if SelectedObjects = nil then
1741 Exit;
1743 b := False;
1744 i := 0;
1746 for a := 0 to High(SelectedObjects) do
1747 with SelectedObjects[a] do
1748 if Live then
1749 begin
1750 if not b then
1751 begin
1752 SetLength(UndoBuffer, Length(UndoBuffer)+1);
1753 i := High(UndoBuffer);
1754 b := True;
1755 end;
1757 SetLength(UndoBuffer[i], Length(UndoBuffer[i])+1);
1758 ii := High(UndoBuffer[i]);
1760 case ObjectType of
1761 OBJECT_PANEL:
1762 begin
1763 UndoBuffer[i, ii].UndoType := UNDO_DELETE_PANEL;
1764 New(UndoBuffer[i, ii].Panel);
1765 UndoBuffer[i, ii].Panel^ := gPanels[ID];
1766 end;
1767 OBJECT_ITEM:
1768 begin
1769 UndoBuffer[i, ii].UndoType := UNDO_DELETE_ITEM;
1770 UndoBuffer[i, ii].Item := gItems[ID];
1771 end;
1772 OBJECT_AREA:
1773 begin
1774 UndoBuffer[i, ii].UndoType := UNDO_DELETE_AREA;
1775 UndoBuffer[i, ii].Area := gAreas[ID];
1776 end;
1777 OBJECT_TRIGGER:
1778 begin
1779 UndoBuffer[i, ii].UndoType := UNDO_DELETE_TRIGGER;
1780 UndoBuffer[i, ii].Trigger := gTriggers[ID];
1781 end;
1782 end;
1784 RemoveObject(ID, ObjectType);
1785 end;
1787 RemoveSelectFromObjects();
1789 MainForm.miUndo.Enabled := UndoBuffer <> nil;
1790 MainForm.RecountSelectedObjects();
1791 end;
1793 procedure Undo_Add(ObjectType: Byte; ID: DWORD; Group: Boolean = False);
1794 var
1795 i, ii: Integer;
1796 begin
1797 if (not Group) or (Length(UndoBuffer) = 0) then
1798 SetLength(UndoBuffer, Length(UndoBuffer)+1);
1799 SetLength(UndoBuffer[High(UndoBuffer)], Length(UndoBuffer[High(UndoBuffer)])+1);
1800 i := High(UndoBuffer);
1801 ii := High(UndoBuffer[i]);
1803 case ObjectType of
1804 OBJECT_PANEL:
1805 UndoBuffer[i, ii].UndoType := UNDO_ADD_PANEL;
1806 OBJECT_ITEM:
1807 UndoBuffer[i, ii].UndoType := UNDO_ADD_ITEM;
1808 OBJECT_MONSTER:
1809 UndoBuffer[i, ii].UndoType := UNDO_ADD_MONSTER;
1810 OBJECT_AREA:
1811 UndoBuffer[i, ii].UndoType := UNDO_ADD_AREA;
1812 OBJECT_TRIGGER:
1813 UndoBuffer[i, ii].UndoType := UNDO_ADD_TRIGGER;
1814 end;
1816 UndoBuffer[i, ii].AddID := ID;
1818 MainForm.miUndo.Enabled := UndoBuffer <> nil;
1819 end;
1821 procedure FullClear();
1822 begin
1823 RemoveSelectFromObjects();
1824 ClearMap();
1825 LoadSky(gMapInfo.SkyName);
1826 UndoBuffer := nil;
1827 slInvalidTextures.Clear();
1828 MapCheckForm.lbErrorList.Clear();
1829 MapCheckForm.mErrorDescription.Clear();
1831 MainForm.miUndo.Enabled := False;
1832 MainForm.sbHorizontal.Position := 0;
1833 MainForm.sbVertical.Position := 0;
1834 MainForm.FormResize(nil);
1835 MainForm.Caption := FormCaption;
1836 OpenedMap := '';
1837 OpenedWAD := '';
1838 end;
1840 procedure ErrorMessageBox(str: String);
1841 begin
1842 MessageBox(0, PChar(str), PChar(_lc[I_MSG_ERROR]),
1843 MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1);
1844 end;
1846 function CheckProperty(): Boolean;
1847 var
1848 _id: Integer;
1849 begin
1850 Result := False;
1852 _id := GetFirstSelected();
1854 if SelectedObjects[_id].ObjectType = OBJECT_PANEL then
1855 with gPanels[SelectedObjects[_id].ID] do
1856 begin
1857 if TextureWidth <> 0 then
1858 if StrToIntDef(MainForm.vleObjectProperty.Values[_lc[I_PROP_WIDTH]], 1) mod TextureWidth <> 0 then
1859 begin
1860 ErrorMessageBox(Format(_lc[I_MSG_WRONG_TEXWIDTH],
1861 [TextureWidth]));
1862 Exit;
1863 end;
1865 if TextureHeight <> 0 then
1866 if StrToIntDef(Trim(MainForm.vleObjectProperty.Values[_lc[I_PROP_HEIGHT]]), 1) mod TextureHeight <> 0 then
1867 begin
1868 ErrorMessageBox(Format(_lc[I_MSG_WRONG_TEXHEIGHT],
1869 [TextureHeight]));
1870 Exit;
1871 end;
1873 if IsTexturedPanel(PanelType) and (TextureName <> '') then
1874 if not (StrToIntDef(MainForm.vleObjectProperty.Values[_lc[I_PROP_PANEL_ALPHA]], -1) in [0..255]) then
1875 begin
1876 ErrorMessageBox(_lc[I_MSG_WRONG_ALPHA]);
1877 Exit;
1878 end;
1879 end;
1881 if SelectedObjects[_id].ObjectType in [OBJECT_PANEL, OBJECT_TRIGGER] then
1882 if (StrToIntDef(MainForm.vleObjectProperty.Values[_lc[I_PROP_WIDTH]], 0) <= 0) or
1883 (StrToIntDef(MainForm.vleObjectProperty.Values[_lc[I_PROP_HEIGHT]], 0) <= 0) then
1884 begin
1885 ErrorMessageBox(_lc[I_MSG_WRONG_SIZE]);
1886 Exit;
1887 end;
1889 if (Trim(MainForm.vleObjectProperty.Values[_lc[I_PROP_X]]) = '') or
1890 (Trim(MainForm.vleObjectProperty.Values[_lc[I_PROP_Y]]) = '') then
1891 begin
1892 ErrorMessageBox(_lc[I_MSG_WRONG_XY]);
1893 Exit;
1894 end;
1896 Result := True;
1897 end;
1899 procedure SelectTexture(ID: Integer);
1900 begin
1901 MainForm.lbTextureList.ItemIndex := ID;
1902 MainForm.lbTextureListClick(nil);
1903 end;
1905 function AddTexture(aWAD, aSection, aTex: String; silent: Boolean): Boolean;
1906 var
1907 a, FrameLen: Integer;
1908 ok: Boolean;
1909 FileName: String;
1910 ResourceName: String;
1911 FullResourceName: String;
1912 SectionName: String;
1913 Data: Pointer;
1914 Width, Height: Word;
1915 fn: String;
1916 begin
1917 Data := nil;
1918 FrameLen := 0;
1919 Width := 0;
1920 Height := 0;
1922 if aSection = '..' then
1923 SectionName := ''
1924 else
1925 SectionName := aSection;
1927 if aWAD = '' then
1928 aWAD := _lc[I_WAD_SPECIAL_MAP];
1930 if aWAD = _lc[I_WAD_SPECIAL_MAP] then
1931 begin // Файл карты
1932 g_ProcessResourceStr(OpenedMap, @fn, nil, nil);
1933 //FileName := EditorDir+'maps\'+ExtractFileName(fn);
1934 FileName := fn;
1935 ResourceName := ':'+SectionName+'\'+aTex;
1936 end
1937 else
1938 if aWAD = _lc[I_WAD_SPECIAL_TEXS] then
1939 begin // Спец. текстуры
1940 FileName := '';
1941 ResourceName := aTex;
1942 end
1943 else
1944 begin // Внешний WAD
1945 FileName := EditorDir+'wads/'+aWAD;
1946 ResourceName := aWAD+':'+SectionName+'\'+aTex;
1947 end;
1949 ok := True;
1951 // Есть ли уже такая текстура:
1952 for a := 0 to MainForm.lbTextureList.Items.Count-1 do
1953 if ResourceName = MainForm.lbTextureList.Items[a] then
1954 begin
1955 if not silent then
1956 ErrorMessageBox(Format(_lc[I_MSG_TEXTURE_ALREADY],
1957 [ResourceName]));
1958 ok := False;
1959 end;
1961 // Название ресурса <= 64 символов:
1962 if Length(ResourceName) > 64 then
1963 begin
1964 if not silent then
1965 ErrorMessageBox(Format(_lc[I_MSG_RES_NAME_64],
1966 [ResourceName]));
1967 ok := False;
1968 end;
1970 if ok then
1971 begin
1972 a := -1;
1973 if aWAD = _lc[I_WAD_SPECIAL_TEXS] then
1974 begin
1975 a := MainForm.lbTextureList.Items.Add(ResourceName);
1976 if not silent then
1977 SelectTexture(a);
1978 Result := True;
1979 Exit;
1980 end;
1982 FullResourceName := FileName+':'+SectionName+'\'+aTex;
1984 if IsAnim(FullResourceName) then
1985 begin // Аним. текстура
1986 GetFrame(FullResourceName, Data, FrameLen, Width, Height);
1988 if not g_CreateTextureMemorySize(Data, FrameLen, ResourceName, 0, 0, Width, Height, 1) then
1989 ok := False;
1990 a := MainForm.lbTextureList.Items.Add(ResourceName);
1991 end
1992 else // Обычная текстура
1993 begin
1994 if not g_CreateTextureWAD(ResourceName, FullResourceName) then
1995 ok := False;
1996 a := MainForm.lbTextureList.Items.Add(ResourceName);
1997 end;
1998 if (not ok) and (slInvalidTextures.IndexOf(ResourceName) = -1) then
1999 begin
2000 slInvalidTextures.Add(ResourceName);
2001 ok := True;
2002 end;
2003 if (a > -1) and (not silent) then
2004 SelectTexture(a);
2005 end;
2007 Result := ok;
2008 end;
2010 procedure UpdateCaption(sMap, sFile, sRes: String);
2011 begin
2012 with MainForm do
2013 if (sFile = '') and (sRes = '') and (sMap = '') then
2014 Caption := FormCaption
2015 else
2016 if sMap = '' then
2017 Caption := Format('%s - %s:%s', [FormCaption, sFile, sRes])
2018 else
2019 if (sFile <> '') and (sRes <> '') then
2020 Caption := Format('%s - %s (%s:%s)', [FormCaption, sMap, sFile, sRes])
2021 else
2022 Caption := Format('%s - %s', [FormCaption, sMap]);
2023 end;
2025 procedure OpenMap(FileName: String; mapN: String);
2026 var
2027 MapName: String;
2028 idx: Integer;
2029 begin
2030 SelectMapForm.Caption := _lc[I_CAP_OPEN];
2031 SelectMapForm.GetMaps(FileName);
2033 if (FileName = OpenedWAD) and
2034 (OpenedMap <> '') then
2035 begin
2036 MapName := OpenedMap;
2037 while (Pos(':\', MapName) > 0) do
2038 Delete(MapName, 1, Pos(':\', MapName) + 1);
2040 idx := SelectMapForm.lbMapList.Items.IndexOf(MapName);
2041 SelectMapForm.lbMapList.ItemIndex := idx;
2042 end
2043 else
2044 if SelectMapForm.lbMapList.Count > 0 then
2045 SelectMapForm.lbMapList.ItemIndex := 0
2046 else
2047 SelectMapForm.lbMapList.ItemIndex := -1;
2049 if mapN = '' then
2050 idx := -1
2051 else
2052 idx := SelectMapForm.lbMapList.Items.IndexOf(mapN);
2054 if idx < 0 then
2055 begin
2056 if (SelectMapForm.ShowModal() = mrOK) and
2057 (SelectMapForm.lbMapList.ItemIndex <> -1) then
2058 idx := SelectMapForm.lbMapList.ItemIndex
2059 else
2060 Exit;
2061 end;
2063 MapName := SelectMapForm.lbMapList.Items[idx];
2065 with MainForm do
2066 begin
2067 FullClear();
2069 pLoadProgress.Left := (RenderPanel.Width div 2)-(pLoadProgress.Width div 2);
2070 pLoadProgress.Top := (RenderPanel.Height div 2)-(pLoadProgress.Height div 2);
2071 pLoadProgress.Show();
2073 OpenedMap := FileName+':\'+MapName;
2074 OpenedWAD := FileName;
2076 idx := RecentFiles.IndexOf(OpenedMap);
2077 // Такая карта уже недавно открывалась:
2078 if idx >= 0 then
2079 RecentFiles.Delete(idx);
2080 RecentFiles.Insert(0, OpenedMap);
2081 RefreshRecentMenu();
2083 LoadMap(OpenedMap);
2085 pLoadProgress.Hide();
2086 FormResize(nil);
2088 lbTextureList.Sorted := True;
2089 lbTextureList.Sorted := False;
2091 UpdateCaption(gMapInfo.Name, ExtractFileName(FileName), MapName);
2092 end;
2093 end;
2095 procedure MoveSelectedObjects(Wall, alt: Boolean; dx, dy: Integer);
2096 var
2097 okX, okY: Boolean;
2098 a: Integer;
2099 begin
2100 if SelectedObjects = nil then
2101 Exit;
2103 okX := True;
2104 okY := True;
2106 if Wall then
2107 for a := 0 to High(SelectedObjects) do
2108 if SelectedObjects[a].Live then
2109 begin
2110 if ObjectCollideLevel(SelectedObjects[a].ID, SelectedObjects[a].ObjectType, dx, 0) then
2111 okX := False;
2113 if ObjectCollideLevel(SelectedObjects[a].ID, SelectedObjects[a].ObjectType, 0, dy) then
2114 okY := False;
2116 if (not okX) or (not okY) then
2117 Break;
2118 end;
2120 if okX or okY then
2121 begin
2122 for a := 0 to High(SelectedObjects) do
2123 if SelectedObjects[a].Live then
2124 begin
2125 if okX then
2126 MoveObject(SelectedObjects[a].ObjectType, SelectedObjects[a].ID, dx, 0);
2128 if okY then
2129 MoveObject(SelectedObjects[a].ObjectType, SelectedObjects[a].ID, 0, dy);
2131 if alt and (SelectedObjects[a].ObjectType = OBJECT_TRIGGER) then
2132 begin
2133 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_PRESS,
2134 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
2135 begin // Двигаем зону Расширителя
2136 if okX then
2137 gTriggers[SelectedObjects[a].ID].Data.tX := gTriggers[SelectedObjects[a].ID].Data.tX+dx;
2138 if okY then
2139 gTriggers[SelectedObjects[a].ID].Data.tY := gTriggers[SelectedObjects[a].ID].Data.tY+dy;
2140 end;
2142 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_TELEPORT] then
2143 begin // Двигаем точку назначения Телепорта
2144 if okX then
2145 gTriggers[SelectedObjects[a].ID].Data.TargetPoint.X := gTriggers[SelectedObjects[a].ID].Data.TargetPoint.X+dx;
2146 if okY then
2147 gTriggers[SelectedObjects[a].ID].Data.TargetPoint.Y := gTriggers[SelectedObjects[a].ID].Data.TargetPoint.Y+dy;
2148 end;
2150 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_SPAWNMONSTER] then
2151 begin // Двигаем точку создания монстра
2152 if okX then
2153 gTriggers[SelectedObjects[a].ID].Data.MonPos.X := gTriggers[SelectedObjects[a].ID].Data.MonPos.X+dx;
2154 if okY then
2155 gTriggers[SelectedObjects[a].ID].Data.MonPos.Y := gTriggers[SelectedObjects[a].ID].Data.MonPos.Y+dy;
2156 end;
2158 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_SPAWNITEM] then
2159 begin // Двигаем точку создания предмета
2160 if okX then
2161 gTriggers[SelectedObjects[a].ID].Data.ItemPos.X := gTriggers[SelectedObjects[a].ID].Data.ItemPos.X+dx;
2162 if okY then
2163 gTriggers[SelectedObjects[a].ID].Data.ItemPos.Y := gTriggers[SelectedObjects[a].ID].Data.ItemPos.Y+dy;
2164 end;
2166 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_SHOT] then
2167 begin // Двигаем точку создания выстрела
2168 if okX then
2169 gTriggers[SelectedObjects[a].ID].Data.ShotPos.X := gTriggers[SelectedObjects[a].ID].Data.ShotPos.X+dx;
2170 if okY then
2171 gTriggers[SelectedObjects[a].ID].Data.ShotPos.Y := gTriggers[SelectedObjects[a].ID].Data.ShotPos.Y+dy;
2172 end;
2173 end;
2174 end;
2176 LastMovePoint := MousePos;
2177 end;
2178 end;
2180 procedure ShowLayer(Layer: Byte; show: Boolean);
2181 begin
2182 LayerEnabled[Layer] := show;
2184 case Layer of
2185 LAYER_BACK:
2186 begin
2187 MainForm.miLayer1.Checked := show;
2188 MainForm.miLayerP1.Checked := show;
2189 end;
2190 LAYER_WALLS:
2191 begin
2192 MainForm.miLayer2.Checked := show;
2193 MainForm.miLayerP2.Checked := show;
2194 end;
2195 LAYER_FOREGROUND:
2196 begin
2197 MainForm.miLayer3.Checked := show;
2198 MainForm.miLayerP3.Checked := show;
2199 end;
2200 LAYER_STEPS:
2201 begin
2202 MainForm.miLayer4.Checked := show;
2203 MainForm.miLayerP4.Checked := show;
2204 end;
2205 LAYER_WATER:
2206 begin
2207 MainForm.miLayer5.Checked := show;
2208 MainForm.miLayerP5.Checked := show;
2209 end;
2210 LAYER_ITEMS:
2211 begin
2212 MainForm.miLayer6.Checked := show;
2213 MainForm.miLayerP6.Checked := show;
2214 end;
2215 LAYER_MONSTERS:
2216 begin
2217 MainForm.miLayer7.Checked := show;
2218 MainForm.miLayerP7.Checked := show;
2219 end;
2220 LAYER_AREAS:
2221 begin
2222 MainForm.miLayer8.Checked := show;
2223 MainForm.miLayerP8.Checked := show;
2224 end;
2225 LAYER_TRIGGERS:
2226 begin
2227 MainForm.miLayer9.Checked := show;
2228 MainForm.miLayerP9.Checked := show;
2229 end;
2230 end;
2232 RemoveSelectFromObjects();
2233 end;
2235 procedure SwitchLayer(Layer: Byte);
2236 begin
2237 ShowLayer(Layer, not LayerEnabled[Layer]);
2238 end;
2240 procedure SwitchMap();
2241 begin
2242 ShowMap := not ShowMap;
2243 MainForm.tbShowMap.Down := ShowMap;
2244 end;
2246 procedure ShowEdges();
2247 begin
2248 if drEdge[3] < 255 then
2249 drEdge[3] := 255
2250 else
2251 drEdge[3] := gAlphaEdge;
2252 end;
2254 function SelectedTexture(): String;
2255 begin
2256 if MainForm.lbTextureList.ItemIndex <> -1 then
2257 Result := MainForm.lbTextureList.Items[MainForm.lbTextureList.ItemIndex]
2258 else
2259 Result := '';
2260 end;
2262 function IsSpecialTextureSel(): Boolean;
2263 begin
2264 Result := (MainForm.lbTextureList.ItemIndex <> -1) and
2265 IsSpecialTexture(MainForm.lbTextureList.Items[MainForm.lbTextureList.ItemIndex]);
2266 end;
2268 function CopyBufferToString(var CopyBuf: TCopyRecArray): String;
2269 var
2270 i, j: Integer;
2271 Res: String;
2273 procedure AddInt(x: Integer);
2274 begin
2275 Res := Res + IntToStr(x) + ' ';
2276 end;
2278 begin
2279 Result := '';
2281 if Length(CopyBuf) = 0 then
2282 Exit;
2284 Res := CLIPBOARD_SIG + ' ';
2286 for i := 0 to High(CopyBuf) do
2287 begin
2288 if (CopyBuf[i].ObjectType = OBJECT_PANEL) and
2289 (CopyBuf[i].Panel = nil) then
2290 Continue;
2292 // Тип объекта:
2293 AddInt(CopyBuf[i].ObjectType);
2294 Res := Res + '; ';
2296 // Свойства объекта:
2297 case CopyBuf[i].ObjectType of
2298 OBJECT_PANEL:
2299 with CopyBuf[i].Panel^ do
2300 begin
2301 AddInt(PanelType);
2302 AddInt(X);
2303 AddInt(Y);
2304 AddInt(Width);
2305 AddInt(Height);
2306 Res := Res + '"' + TextureName + '" ';
2307 AddInt(Alpha);
2308 AddInt(IfThen(Blending, 1, 0));
2309 end;
2311 OBJECT_ITEM:
2312 with CopyBuf[i].Item do
2313 begin
2314 AddInt(ItemType);
2315 AddInt(X);
2316 AddInt(Y);
2317 AddInt(IfThen(OnlyDM, 1, 0));
2318 AddInt(IfThen(Fall, 1, 0));
2319 end;
2321 OBJECT_MONSTER:
2322 with CopyBuf[i].Monster do
2323 begin
2324 AddInt(MonsterType);
2325 AddInt(X);
2326 AddInt(Y);
2327 AddInt(IfThen(Direction = D_LEFT, 1, 0));
2328 end;
2330 OBJECT_AREA:
2331 with CopyBuf[i].Area do
2332 begin
2333 AddInt(AreaType);
2334 AddInt(X);
2335 AddInt(Y);
2336 AddInt(IfThen(Direction = D_LEFT, 1, 0));
2337 end;
2339 OBJECT_TRIGGER:
2340 with CopyBuf[i].Trigger do
2341 begin
2342 AddInt(TriggerType);
2343 AddInt(X);
2344 AddInt(Y);
2345 AddInt(Width);
2346 AddInt(Height);
2347 AddInt(ActivateType);
2348 AddInt(Key);
2349 AddInt(IfThen(Enabled, 1, 0));
2350 AddInt(TexturePanel);
2352 for j := 0 to 127 do
2353 AddInt(Data.Default[j]);
2354 end;
2355 end;
2356 end;
2358 Result := Res;
2359 end;
2361 procedure StringToCopyBuffer(Str: String; var CopyBuf: TCopyRecArray;
2362 var pmin: TPoint);
2363 var
2364 i, j, t: Integer;
2366 function GetNext(): String;
2367 var
2368 p: Integer;
2370 begin
2371 if Str[1] = '"' then
2372 begin
2373 Delete(Str, 1, 1);
2374 p := Pos('"', Str);
2376 if p = 0 then
2377 begin
2378 Result := Str;
2379 Str := '';
2380 end
2381 else
2382 begin
2383 Result := Copy(Str, 1, p-1);
2384 Delete(Str, 1, p);
2385 Str := Trim(Str);
2386 end;
2387 end
2388 else
2389 begin
2390 p := Pos(' ', Str);
2392 if p = 0 then
2393 begin
2394 Result := Str;
2395 Str := '';
2396 end
2397 else
2398 begin
2399 Result := Copy(Str, 1, p-1);
2400 Delete(Str, 1, p);
2401 Str := Trim(Str);
2402 end;
2403 end;
2404 end;
2406 begin
2407 Str := Trim(Str);
2409 if GetNext() <> CLIPBOARD_SIG then
2410 Exit;
2412 while Str <> '' do
2413 begin
2414 // Тип объекта:
2415 t := StrToIntDef(GetNext(), 0);
2417 if (t < OBJECT_PANEL) or (t > OBJECT_TRIGGER) or
2418 (GetNext() <> ';') then
2419 begin // Что-то не то => пропускаем:
2420 t := Pos(';', Str);
2421 Delete(Str, 1, t);
2422 Str := Trim(Str);
2424 Continue;
2425 end;
2427 i := Length(CopyBuf);
2428 SetLength(CopyBuf, i + 1);
2430 CopyBuf[i].ObjectType := t;
2431 CopyBuf[i].Panel := nil;
2433 // Свойства объекта:
2434 case t of
2435 OBJECT_PANEL:
2436 begin
2437 New(CopyBuf[i].Panel);
2439 with CopyBuf[i].Panel^ do
2440 begin
2441 PanelType := StrToIntDef(GetNext(), PANEL_WALL);
2442 X := StrToIntDef(GetNext(), 0);
2443 Y := StrToIntDef(GetNext(), 0);
2444 pmin.X := Min(X, pmin.X);
2445 pmin.Y := Min(Y, pmin.Y);
2446 Width := StrToIntDef(GetNext(), 16);
2447 Height := StrToIntDef(GetNext(), 16);
2448 TextureName := GetNext();
2449 Alpha := StrToIntDef(GetNext(), 0);
2450 Blending := (GetNext() = '1');
2451 end;
2452 end;
2454 OBJECT_ITEM:
2455 with CopyBuf[i].Item do
2456 begin
2457 ItemType := StrToIntDef(GetNext(), ITEM_MEDKIT_SMALL);
2458 X := StrToIntDef(GetNext(), 0);
2459 Y := StrToIntDef(GetNext(), 0);
2460 pmin.X := Min(X, pmin.X);
2461 pmin.Y := Min(Y, pmin.Y);
2462 OnlyDM := (GetNext() = '1');
2463 Fall := (GetNext() = '1');
2464 end;
2466 OBJECT_MONSTER:
2467 with CopyBuf[i].Monster do
2468 begin
2469 MonsterType := StrToIntDef(GetNext(), MONSTER_DEMON);
2470 X := StrToIntDef(GetNext(), 0);
2471 Y := StrToIntDef(GetNext(), 0);
2472 pmin.X := Min(X, pmin.X);
2473 pmin.Y := Min(Y, pmin.Y);
2475 if GetNext() = '1' then
2476 Direction := D_LEFT
2477 else
2478 Direction := D_RIGHT;
2479 end;
2481 OBJECT_AREA:
2482 with CopyBuf[i].Area do
2483 begin
2484 AreaType := StrToIntDef(GetNext(), AREA_PLAYERPOINT1);
2485 X := StrToIntDef(GetNext(), 0);
2486 Y := StrToIntDef(GetNext(), 0);
2487 pmin.X := Min(X, pmin.X);
2488 pmin.Y := Min(Y, pmin.Y);
2489 if GetNext() = '1' then
2490 Direction := D_LEFT
2491 else
2492 Direction := D_RIGHT;
2493 end;
2495 OBJECT_TRIGGER:
2496 with CopyBuf[i].Trigger do
2497 begin
2498 TriggerType := StrToIntDef(GetNext(), TRIGGER_EXIT);
2499 X := StrToIntDef(GetNext(), 0);
2500 Y := StrToIntDef(GetNext(), 0);
2501 pmin.X := Min(X, pmin.X);
2502 pmin.Y := Min(Y, pmin.Y);
2503 Width := StrToIntDef(GetNext(), 16);
2504 Height := StrToIntDef(GetNext(), 16);
2505 ActivateType := StrToIntDef(GetNext(), 0);
2506 Key := StrToIntDef(GetNext(), 0);
2507 Enabled := (GetNext() = '1');
2508 TexturePanel := StrToIntDef(GetNext(), 0);
2510 for j := 0 to 127 do
2511 Data.Default[j] := StrToIntDef(GetNext(), 0);
2513 case TriggerType of
2514 TRIGGER_TELEPORT:
2515 begin
2516 pmin.X := Min(Data.TargetPoint.X, pmin.X);
2517 pmin.Y := Min(Data.TargetPoint.Y, pmin.Y);
2518 end;
2519 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
2520 begin
2521 pmin.X := Min(Data.tX, pmin.X);
2522 pmin.Y := Min(Data.tY, pmin.Y);
2523 end;
2524 TRIGGER_SPAWNMONSTER:
2525 begin
2526 pmin.X := Min(Data.MonPos.X, pmin.X);
2527 pmin.Y := Min(Data.MonPos.Y, pmin.Y);
2528 end;
2529 TRIGGER_SPAWNITEM:
2530 begin
2531 pmin.X := Min(Data.ItemPos.X, pmin.X);
2532 pmin.Y := Min(Data.ItemPos.Y, pmin.Y);
2533 end;
2534 TRIGGER_SHOT:
2535 begin
2536 pmin.X := Min(Data.ShotPos.X, pmin.X);
2537 pmin.Y := Min(Data.ShotPos.Y, pmin.Y);
2538 end;
2539 end;
2540 end;
2541 end;
2542 end;
2543 end;
2545 //----------------------------------------
2546 //Закончились вспомогательные процедуры
2547 //----------------------------------------
2549 procedure TMainForm.RefreshRecentMenu();
2550 var
2551 i: Integer;
2552 MI: TMenuItem;
2553 begin
2554 // Лишние запомненные карты:
2555 while RecentFiles.Count > RecentCount do
2556 RecentFiles.Delete(RecentFiles.Count-1);
2558 // Лишние строки меню:
2559 while MainMenu.Items[0].Count > RECENT_FILES_MENU_START do
2560 MainMenu.Items[0].Delete(MainMenu.Items[0].Count-1);
2562 // Отделение списка карт от строки "Выход":
2563 if RecentFiles.Count > 0 then
2564 begin
2565 MI := TMenuItem.Create(MainMenu.Items[0]);
2566 MI.Caption := '-';
2567 MainMenu.Items[0].Add(MI);
2568 end;
2570 // Добавление в меню списка запомненных карт:
2571 for i := 0 to RecentFiles.Count-1 do
2572 begin
2573 MI := TMenuItem.Create(MainMenu.Items[0]);
2574 MI.Caption := IntToStr(i+1) + ' ' + RecentFiles[i];
2575 MI.OnClick := aRecentFileExecute;
2576 MainMenu.Items[0].Add(MI);
2577 end;
2578 end;
2580 procedure TMainForm.aRecentFileExecute(Sender: TObject);
2581 var
2582 n: Integer;
2583 fn, s: String;
2584 begin
2585 s := LowerCase((Sender as TMenuItem).Caption);
2586 Delete(s, Pos('&', s), 1);
2587 s := Trim(Copy(s, 1, 2));
2588 n := StrToIntDef(s, 0) - 1;
2589 if (n >= 0) and (n <= RecentFiles.Count) then
2590 begin
2591 fn := g_ExtractWadName(RecentFiles[n]);
2592 if FileExists(fn) then
2593 begin
2594 s := g_ExtractFilePathName(RecentFiles[n]);
2595 OpenMap(fn, s)
2596 end
2597 else if MessageBox(0, PChar(_lc[I_MSG_DEL_RECENT_PROMT]), PChar(_lc[I_MSG_DEL_RECENT]), MB_ICONQUESTION or MB_YESNO) = idYes then
2598 begin
2599 RecentFiles.Delete(n);
2600 RefreshRecentMenu();
2601 end
2602 end
2603 end;
2605 procedure TMainForm.aEditorOptionsExecute(Sender: TObject);
2606 begin
2607 OptionsForm.ShowModal();
2608 end;
2610 procedure LoadStdFont(cfgres, texture: string; var FontID: DWORD);
2611 var
2612 cwdt, chgt: Byte;
2613 spc: ShortInt;
2614 ID: DWORD;
2615 cfgdata: Pointer;
2616 cfglen: Integer;
2617 config: TConfig;
2618 begin
2619 ID := 0;
2620 g_ReadResource(EditorDir + 'data/game.wad', 'FONTS', cfgres, cfgdata, cfglen);
2621 if cfgdata <> nil then
2622 begin
2623 if not g_CreateTextureWAD('FONT_STD', EditorDir + 'data/game.wad:FONTS\' + texture) then
2624 e_WriteLog('ERROR ERROR ERROR', MSG_WARNING);
2626 config := TConfig.CreateMem(cfgdata, cfglen);
2627 cwdt := Min(Max(config.ReadInt('FontMap', 'CharWidth', 0), 0), 255);
2628 chgt := Min(Max(config.ReadInt('FontMap', 'CharHeight', 0), 0), 255);
2629 spc := Min(Max(config.ReadInt('FontMap', 'Kerning', 0), -128), 127);
2631 if g_GetTexture('FONT_STD', ID) then
2632 e_TextureFontBuild(ID, FontID, cwdt, chgt, spc - 2);
2634 config.Free();
2635 FreeMem(cfgdata)
2636 end
2637 else
2638 begin
2639 e_WriteLog('Could not load FONT_STD', MSG_WARNING)
2640 end
2641 end;
2643 procedure TMainForm.FormCreate(Sender: TObject);
2644 var
2645 config: TConfig;
2646 i: Integer;
2647 s: String;
2648 begin
2649 Randomize();
2651 EditorDir := ExtractFilePath(Application.ExeName);
2653 e_WriteLog('Doom 2D: Forever Editor version ' + EDITOR_VERSION, MSG_NOTIFY);
2654 e_WriteLog('Build date: ' + EDITOR_BUILDDATE + ' ' + EDITOR_BUILDTIME, MSG_NOTIFY);
2655 e_WriteLog('Build hash: ' + g_GetBuildHash(), MSG_NOTIFY);
2656 e_WriteLog('Build by: ' + g_GetBuilderName(), MSG_NOTIFY);
2658 slInvalidTextures := TStringList.Create;
2660 ShowLayer(LAYER_BACK, True);
2661 ShowLayer(LAYER_WALLS, True);
2662 ShowLayer(LAYER_FOREGROUND, True);
2663 ShowLayer(LAYER_STEPS, True);
2664 ShowLayer(LAYER_WATER, True);
2665 ShowLayer(LAYER_ITEMS, True);
2666 ShowLayer(LAYER_MONSTERS, True);
2667 ShowLayer(LAYER_AREAS, True);
2668 ShowLayer(LAYER_TRIGGERS, True);
2670 ClearMap();
2672 FormCaption := MainForm.Caption;
2673 OpenedMap := '';
2674 OpenedWAD := '';
2676 config := TConfig.CreateFile(EditorDir+'Editor.cfg');
2678 if config.ReadInt('Editor', 'XPos', -1) = -1 then
2679 Position := poDesktopCenter
2680 else begin
2681 Left := config.ReadInt('Editor', 'XPos', Left);
2682 Top := config.ReadInt('Editor', 'YPos', Top);
2683 Width := config.ReadInt('Editor', 'Width', Width);
2684 Height := config.ReadInt('Editor', 'Height', Height);
2685 end;
2686 if config.ReadBool('Editor', 'Maximize', False) then
2687 WindowState := wsMaximized;
2688 ShowMap := config.ReadBool('Editor', 'Minimap', False);
2689 PanelProps.Width := config.ReadInt('Editor', 'PanelProps', PanelProps.ClientWidth);
2690 Splitter1.Left := PanelProps.Left;
2691 PanelObjs.Height := config.ReadInt('Editor', 'PanelObjs', PanelObjs.ClientHeight);
2692 Splitter2.Top := PanelObjs.Top;
2693 StatusBar.Top := PanelObjs.BoundsRect.Bottom;
2694 DotEnable := config.ReadBool('Editor', 'DotEnable', True);
2695 DotColor := config.ReadInt('Editor', 'DotColor', $FFFFFF);
2696 DotStepOne := config.ReadInt('Editor', 'DotStepOne', 16);
2697 DotStepTwo := config.ReadInt('Editor', 'DotStepTwo', 8);
2698 DotStep := config.ReadInt('Editor', 'DotStep', DotStepOne);
2699 DrawTexturePanel := config.ReadBool('Editor', 'DrawTexturePanel', True);
2700 DrawPanelSize := config.ReadBool('Editor', 'DrawPanelSize', True);
2701 BackColor := config.ReadInt('Editor', 'BackColor', $7F6040);
2702 PreviewColor := config.ReadInt('Editor', 'PreviewColor', $00FF00);
2703 UseCheckerboard := config.ReadBool('Editor', 'UseCheckerboard', True);
2704 gColorEdge := config.ReadInt('Editor', 'EdgeColor', COLOR_EDGE);
2705 gAlphaEdge := config.ReadInt('Editor', 'EdgeAlpha', ALPHA_EDGE);
2706 if gAlphaEdge = 255 then
2707 gAlphaEdge := ALPHA_EDGE;
2708 drEdge[0] := GetRValue(gColorEdge);
2709 drEdge[1] := GetGValue(gColorEdge);
2710 drEdge[2] := GetBValue(gColorEdge);
2711 if not config.ReadBool('Editor', 'EdgeShow', True) then
2712 drEdge[3] := 255
2713 else
2714 drEdge[3] := gAlphaEdge;
2715 gAlphaTriggerLine := config.ReadInt('Editor', 'LineAlpha', ALPHA_LINE);
2716 if gAlphaTriggerLine = 255 then
2717 gAlphaTriggerLine := ALPHA_LINE;
2718 gAlphaTriggerArea := config.ReadInt('Editor', 'TriggerAlpha', ALPHA_AREA);
2719 if gAlphaTriggerArea = 255 then
2720 gAlphaTriggerArea := ALPHA_AREA;
2721 gAlphaMonsterRect := config.ReadInt('Editor', 'MonsterRectAlpha', 0);
2722 gAlphaAreaRect := config.ReadInt('Editor', 'AreaRectAlpha', 0);
2723 if config.ReadInt('Editor', 'Scale', 0) = 1 then
2724 Scale := 2
2725 else
2726 Scale := 1;
2727 if config.ReadInt('Editor', 'DotSize', 0) = 1 then
2728 DotSize := 2
2729 else
2730 DotSize := 1;
2731 OpenDialog.InitialDir := config.ReadStr('Editor', 'LastOpenDir', EditorDir);
2732 SaveDialog.InitialDir := config.ReadStr('Editor', 'LastSaveDir', EditorDir);
2734 s := config.ReadStr('Editor', 'Language', '');
2735 gLanguage := s;
2737 Compress := config.ReadBool('Editor', 'Compress', True);
2738 Backup := config.ReadBool('Editor', 'Backup', True);
2740 RecentCount := config.ReadInt('Editor', 'RecentCount', 5);
2741 if RecentCount > 10 then
2742 RecentCount := 10;
2743 if RecentCount < 2 then
2744 RecentCount := 2;
2746 RecentFiles := TStringList.Create();
2747 for i := 0 to RecentCount-1 do
2748 begin
2749 s := config.ReadStr('RecentFiles', IntToStr(i+1), '');
2750 if s <> '' then
2751 RecentFiles.Add(s);
2752 end;
2753 RefreshRecentMenu();
2755 config.Free();
2757 tbShowMap.Down := ShowMap;
2758 tbGridOn.Down := DotEnable;
2759 pcObjects.ActivePageIndex := 0;
2760 Application.Title := _lc[I_EDITOR_TITLE];
2762 Application.OnIdle := OnIdle;
2763 end;
2765 procedure PrintBlack(X, Y: Integer; Text: string; FontID: DWORD);
2766 begin
2767 // NOTE: all the font printing routines assume CP1251
2768 e_TextureFontPrintEx(X, Y, Text, FontID, 0, 0, 0, 1.0);
2769 end;
2771 procedure TMainForm.Draw();
2772 var
2773 x, y: Integer;
2774 a, b: Integer;
2775 ID, PID: DWORD;
2776 Width, Height: Word;
2777 Rect: TRectWH;
2778 ObjCount: Word;
2779 aX, aY, aX2, aY2, XX, ScaleSz: Integer;
2780 begin
2781 ID := 0;
2782 PID := 0;
2783 Width := 0;
2784 Height := 0;
2786 e_BeginRender();
2788 e_Clear(GL_COLOR_BUFFER_BIT,
2789 GetRValue(BackColor)/255,
2790 GetGValue(BackColor)/255,
2791 GetBValue(BackColor)/255);
2793 DrawMap();
2795 ObjCount := SelectedObjectCount();
2797 // Обводим выделенные объекты красной рамкой:
2798 if ObjCount > 0 then
2799 begin
2800 for a := 0 to High(SelectedObjects) do
2801 if SelectedObjects[a].Live then
2802 begin
2803 Rect := ObjectGetRect(SelectedObjects[a].ObjectType, SelectedObjects[a].ID);
2805 with Rect do
2806 begin
2807 e_DrawQuad(X+MapOffset.X, Y+MapOffset.Y,
2808 X+MapOffset.X+Width-1, Y+MapOffset.Y+Height-1,
2809 255, 0, 0);
2811 // Рисуем точки изменения размеров:
2812 if (ObjCount = 1) and
2813 (SelectedObjects[GetFirstSelected].ObjectType in [OBJECT_PANEL, OBJECT_TRIGGER]) then
2814 begin
2815 e_DrawPoint(5, X+MapOffset.X, Y+MapOffset.Y+(Height div 2), 255, 255, 255);
2816 e_DrawPoint(5, X+MapOffset.X+Width-1, Y+MapOffset.Y+(Height div 2), 255, 255, 255);
2817 e_DrawPoint(5, X+MapOffset.X+(Width div 2), Y+MapOffset.Y, 255, 255, 255);
2818 e_DrawPoint(5, X+MapOffset.X+(Width div 2), Y+MapOffset.Y+Height-1, 255, 255, 255);
2820 e_DrawPoint(3, X+MapOffset.X, Y+MapOffset.Y+(Height div 2), 255, 0, 0);
2821 e_DrawPoint(3, X+MapOffset.X+Width-1, Y+MapOffset.Y+(Height div 2), 255, 0, 0);
2822 e_DrawPoint(3, X+MapOffset.X+(Width div 2), Y+MapOffset.Y, 255, 0, 0);
2823 e_DrawPoint(3, X+MapOffset.X+(Width div 2), Y+MapOffset.Y+Height-1, 255, 0, 0);
2824 end;
2825 end;
2826 end;
2827 end;
2829 // Рисуем сетку:
2830 if DotEnable and (PreviewMode = 0) then
2831 begin
2832 if DotSize = 2 then
2833 a := -1
2834 else
2835 a := 0;
2837 x := MapOffset.X mod DotStep;
2838 y := MapOffset.Y mod DotStep;
2840 while x < RenderPanel.Width do
2841 begin
2842 while y < RenderPanel.Height do
2843 begin
2844 e_DrawPoint(DotSize, x + a, y + a,
2845 GetRValue(DotColor),
2846 GetGValue(DotColor),
2847 GetBValue(DotColor));
2848 y += DotStep;
2849 end;
2850 x += DotStep;
2851 y := MapOffset.Y mod DotStep;
2852 end;
2853 end;
2855 // Превью текстуры:
2856 if (lbTextureList.ItemIndex <> -1) and (cbPreview.Checked) and
2857 (not IsSpecialTextureSel()) and (PreviewMode = 0) then
2858 begin
2859 if not g_GetTexture(SelectedTexture(), ID) then
2860 g_GetTexture('NOTEXTURE', ID);
2861 g_GetTextureSizeByID(ID, Width, Height);
2862 if UseCheckerboard then
2863 begin
2864 if g_GetTexture('PREVIEW', PID) then
2865 e_DrawFill(PID, RenderPanel.Width-Width, RenderPanel.Height-Height, Width div 16 + 1, Height div 16 + 1, 0, True, False);
2866 end else
2867 e_DrawFillQuad(RenderPanel.Width-Width-2, RenderPanel.Height-Height-2,
2868 RenderPanel.Width-1, RenderPanel.Height-1,
2869 GetRValue(PreviewColor), GetGValue(PreviewColor), GetBValue(PreviewColor), 0);
2870 e_Draw(ID, RenderPanel.Width-Width, RenderPanel.Height-Height, 0, True, False);
2871 end;
2873 // Подсказка при выборе точки Телепорта:
2874 if SelectFlag = SELECTFLAG_TELEPORT then
2875 begin
2876 with gTriggers[SelectedObjects[GetFirstSelected()].ID] do
2877 if Data.d2d_teleport then
2878 e_DrawLine(2, MousePos.X-16, MousePos.Y-1,
2879 MousePos.X+16, MousePos.Y-1,
2880 0, 0, 255)
2881 else
2882 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+AreaSize[AREA_DMPOINT].Width-1,
2883 MousePos.Y+AreaSize[AREA_DMPOINT].Height-1, 255, 255, 255);
2885 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 192, 192, 192, 127);
2886 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 255, 255, 255);
2887 PrintBlack(MousePos.X+2, MousePos.Y+2, _glc[I_HINT_TELEPORT], gEditorFont);
2888 end;
2890 // Подсказка при выборе точки появления:
2891 if SelectFlag = SELECTFLAG_SPAWNPOINT then
2892 begin
2893 e_DrawLine(2, MousePos.X-16, MousePos.Y-1,
2894 MousePos.X+16, MousePos.Y-1,
2895 0, 0, 255);
2896 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 192, 192, 192, 127);
2897 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 255, 255, 255);
2898 PrintBlack(MousePos.X+2, MousePos.Y+2, _glc[I_HINT_SPAWN], gEditorFont);
2899 end;
2901 // Подсказка при выборе панели двери:
2902 if SelectFlag = SELECTFLAG_DOOR then
2903 begin
2904 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 192, 192, 192, 127);
2905 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 255, 255, 255);
2906 PrintBlack(MousePos.X+2, MousePos.Y+2, _glc[I_HINT_PANEL_DOOR], gEditorFont);
2907 end;
2909 // Подсказка при выборе панели с текстурой:
2910 if SelectFlag = SELECTFLAG_TEXTURE then
2911 begin
2912 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+196, MousePos.Y+18, 192, 192, 192, 127);
2913 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+196, MousePos.Y+18, 255, 255, 255);
2914 PrintBlack(MousePos.X+2, MousePos.Y+2, _glc[I_HINT_PANEL_TEXTURE], gEditorFont);
2915 end;
2917 // Подсказка при выборе панели индикации выстрела:
2918 if SelectFlag = SELECTFLAG_SHOTPANEL then
2919 begin
2920 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+316, MousePos.Y+18, 192, 192, 192, 127);
2921 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+316, MousePos.Y+18, 255, 255, 255);
2922 PrintBlack(MousePos.X+2, MousePos.Y+2, _glc[I_HINT_PANEL_SHOT], gEditorFont);
2923 end;
2925 // Подсказка при выборе панели лифта:
2926 if SelectFlag = SELECTFLAG_LIFT then
2927 begin
2928 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 192, 192, 192, 127);
2929 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 255, 255, 255);
2930 PrintBlack(MousePos.X+2, MousePos.Y+2, _glc[I_HINT_PANEL_LIFT], gEditorFont);
2931 end;
2933 // Подсказка при выборе монстра:
2934 if SelectFlag = SELECTFLAG_MONSTER then
2935 begin
2936 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+120, MousePos.Y+18, 192, 192, 192, 127);
2937 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+120, MousePos.Y+18, 255, 255, 255);
2938 PrintBlack(MousePos.X+2, MousePos.Y+2, _glc[I_HINT_MONSTER], gEditorFont);
2939 end;
2941 // Подсказка при выборе области воздействия:
2942 if DrawPressRect then
2943 begin
2944 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+204, MousePos.Y+18, 192, 192, 192, 127);
2945 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+204, MousePos.Y+18, 255, 255, 255);
2946 PrintBlack(MousePos.X+2, MousePos.Y+2, _glc[I_HINT_EXT_AREA], gEditorFont);
2947 end;
2949 // Рисуем текстуры, если чертим панель:
2950 if (MouseAction = MOUSEACTION_DRAWPANEL) and (DrawTexturePanel) and
2951 (lbTextureList.ItemIndex <> -1) and (DrawRect <> nil) and
2952 (lbPanelType.ItemIndex in [0..8]) and not IsSpecialTextureSel() then
2953 begin
2954 if not g_GetTexture(SelectedTexture(), ID) then
2955 g_GetTexture('NOTEXTURE', ID);
2956 g_GetTextureSizeByID(ID, Width, Height);
2957 with DrawRect^ do
2958 if (Abs(Right-Left) >= Width) and (Abs(Bottom-Top) >= Height) then
2959 e_DrawFill(ID, Min(Left, Right), Min(Top, Bottom), Abs(Right-Left) div Width,
2960 Abs(Bottom-Top) div Height, 64, True, False);
2961 end;
2963 // Прямоугольник выделения:
2964 if DrawRect <> nil then
2965 with DrawRect^ do
2966 e_DrawQuad(Left, Top, Right-1, Bottom-1, 255, 255, 255);
2968 // Чертим мышью панель/триггер или меняем мышью их размер:
2969 if (((MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER]) and
2970 not(ssCtrl in GetKeyShiftState())) or (MouseAction = MOUSEACTION_RESIZE)) and
2971 (DrawPanelSize) then
2972 begin
2973 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+88, MousePos.Y+33, 192, 192, 192, 127);
2974 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+88, MousePos.Y+33, 255, 255, 255);
2976 if MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER] then
2977 begin // Чертим новый
2978 PrintBlack(MousePos.X+2, MousePos.Y+2, Format(_glc[I_HINT_WIDTH],
2979 [Abs(MousePos.X-MouseLDownPos.X)]), gEditorFont);
2980 PrintBlack(MousePos.X+2, MousePos.Y+16, Format(_glc[I_HINT_HEIGHT],
2981 [Abs(MousePos.Y-MouseLDownPos.Y)]), gEditorFont);
2982 end
2983 else // Растягиваем существующий
2984 if SelectedObjects[GetFirstSelected].ObjectType in [OBJECT_PANEL, OBJECT_TRIGGER] then
2985 begin
2986 if SelectedObjects[GetFirstSelected].ObjectType = OBJECT_PANEL then
2987 begin
2988 Width := gPanels[SelectedObjects[GetFirstSelected].ID].Width;
2989 Height := gPanels[SelectedObjects[GetFirstSelected].ID].Height;
2990 end
2991 else
2992 begin
2993 Width := gTriggers[SelectedObjects[GetFirstSelected].ID].Width;
2994 Height := gTriggers[SelectedObjects[GetFirstSelected].ID].Height;
2995 end;
2997 PrintBlack(MousePos.X+2, MousePos.Y+2, Format(_glc[I_HINT_WIDTH], [Width]),
2998 gEditorFont);
2999 PrintBlack(MousePos.X+2, MousePos.Y+16, Format(_glc[I_HINT_HEIGHT], [Height]),
3000 gEditorFont);
3001 end;
3002 end;
3004 // Ближайшая к курсору мыши точка на сетке:
3005 e_DrawPoint(3, MousePos.X, MousePos.Y, 0, 0, 255);
3007 // Мини-карта:
3008 if ShowMap then
3009 begin
3010 // Сколько пикселов карты в 1 пикселе мини-карты:
3011 ScaleSz := 16 div Scale;
3012 // Размеры мини-карты:
3013 aX := max(gMapInfo.Width div ScaleSz, 1);
3014 aY := max(gMapInfo.Height div ScaleSz, 1);
3015 // X-координата на RenderPanel нулевой x-координаты карты:
3016 XX := RenderPanel.Width - aX - 1;
3017 // Рамка карты:
3018 e_DrawFillQuad(XX-1, 0, RenderPanel.Width-1, aY+1, 0, 0, 0, 0);
3019 e_DrawQuad(XX-1, 0, RenderPanel.Width-1, aY+1, 197, 197, 197);
3021 if gPanels <> nil then
3022 begin
3023 // Рисуем панели:
3024 for a := 0 to High(gPanels) do
3025 with gPanels[a] do
3026 if PanelType <> 0 then
3027 begin
3028 // Левый верхний угол:
3029 aX := XX + (X div ScaleSz);
3030 aY := 1 + (Y div ScaleSz);
3031 // Размеры:
3032 aX2 := max(Width div ScaleSz, 1);
3033 aY2 := max(Height div ScaleSz, 1);
3034 // Правый нижний угол:
3035 aX2 := aX + aX2 - 1;
3036 aY2 := aY + aY2 - 1;
3038 case PanelType of
3039 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
3040 PANEL_WATER: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
3041 PANEL_ACID1: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
3042 PANEL_ACID2: e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
3043 PANEL_STEP: e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
3044 PANEL_LIFTUP: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
3045 PANEL_LIFTDOWN: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
3046 PANEL_LIFTLEFT: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
3047 PANEL_LIFTRIGHT: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
3048 PANEL_OPENDOOR: e_DrawFillQuad(aX, aY, aX2, aY2, 100, 220, 92, 0);
3049 PANEL_CLOSEDOOR: e_DrawFillQuad(aX, aY, aX2, aY2, 212, 184, 64, 0);
3050 PANEL_BLOCKMON: e_DrawFillQuad(aX, aY, aX2, aY2, 192, 0, 192, 0);
3051 end;
3052 end;
3054 // Рисуем красным выделенные панели:
3055 if SelectedObjects <> nil then
3056 for b := 0 to High(SelectedObjects) do
3057 with SelectedObjects[b] do
3058 if Live and (ObjectType = OBJECT_PANEL) then
3059 with gPanels[SelectedObjects[b].ID] do
3060 if PanelType and not(PANEL_BACK or PANEL_FORE) <> 0 then
3061 begin
3062 // Левый верхний угол:
3063 aX := XX + (X div ScaleSz);
3064 aY := 1 + (Y div ScaleSz);
3065 // Размеры:
3066 aX2 := max(Width div ScaleSz, 1);
3067 aY2 := max(Height div ScaleSz, 1);
3068 // Правый нижний угол:
3069 aX2 := aX + aX2 - 1;
3070 aY2 := aY + aY2 - 1;
3072 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0)
3073 end;
3074 end;
3076 if (gMapInfo.Width > RenderPanel.Width) or
3077 (gMapInfo.Height > RenderPanel.Height) then
3078 begin
3079 // Окно, показывающее текущее положение экрана на карте:
3080 // Размеры окна:
3081 x := max(min(RenderPanel.Width, gMapInfo.Width) div ScaleSz, 1);
3082 y := max(min(RenderPanel.Height, gMapInfo.Height) div ScaleSz, 1);
3083 // Левый верхний угол:
3084 aX := XX + ((-MapOffset.X) div ScaleSz);
3085 aY := 1 + ((-MapOffset.Y) div ScaleSz);
3086 // Правый нижний угол:
3087 aX2 := aX + x - 1;
3088 aY2 := aY + y - 1;
3090 e_DrawFillQuad(aX, aY, aX2, aY2, 127, 192, 127, 127, B_BLEND);
3091 e_DrawQuad(aX, aY, aX2, aY2, 255, 0, 0);
3092 end;
3093 end; // Мини-карта
3095 e_EndRender();
3096 RenderPanel.SwapBuffers();
3097 end;
3099 procedure TMainForm.FormResize(Sender: TObject);
3100 begin
3101 e_SetViewPort(0, 0, RenderPanel.Width, RenderPanel.Height);
3103 sbHorizontal.Min := Min(gMapInfo.Width - RenderPanel.Width, -RenderPanel.Width div 2);
3104 sbHorizontal.Max := Max(0, gMapInfo.Width - RenderPanel.Width div 2);
3105 sbVertical.Min := Min(gMapInfo.Height - RenderPanel.Height, -RenderPanel.Height div 2);
3106 sbVertical.Max := Max(0, gMapInfo.Height - RenderPanel.Height div 2);
3108 MapOffset.X := -sbHorizontal.Position;
3109 MapOffset.Y := -sbVertical.Position;
3110 end;
3112 procedure SelectNextObject(X, Y: Integer; ObjectType: Byte; ID: DWORD);
3113 var
3114 j, j_max: Integer;
3115 res: Boolean;
3116 begin
3117 j_max := 0; // shut up compiler
3118 case ObjectType of
3119 OBJECT_PANEL:
3120 begin
3121 res := (gPanels <> nil) and
3122 PanelInShownLayer(gPanels[ID].PanelType) and
3123 g_CollidePoint(X, Y, gPanels[ID].X, gPanels[ID].Y,
3124 gPanels[ID].Width,
3125 gPanels[ID].Height);
3126 j_max := Length(gPanels) - 1;
3127 end;
3129 OBJECT_ITEM:
3130 begin
3131 res := (gItems <> nil) and
3132 LayerEnabled[LAYER_ITEMS] and
3133 g_CollidePoint(X, Y, gItems[ID].X, gItems[ID].Y,
3134 ItemSize[gItems[ID].ItemType][0],
3135 ItemSize[gItems[ID].ItemType][1]);
3136 j_max := Length(gItems) - 1;
3137 end;
3139 OBJECT_MONSTER:
3140 begin
3141 res := (gMonsters <> nil) and
3142 LayerEnabled[LAYER_MONSTERS] and
3143 g_CollidePoint(X, Y, gMonsters[ID].X, gMonsters[ID].Y,
3144 MonsterSize[gMonsters[ID].MonsterType].Width,
3145 MonsterSize[gMonsters[ID].MonsterType].Height);
3146 j_max := Length(gMonsters) - 1;
3147 end;
3149 OBJECT_AREA:
3150 begin
3151 res := (gAreas <> nil) and
3152 LayerEnabled[LAYER_AREAS] and
3153 g_CollidePoint(X, Y, gAreas[ID].X, gAreas[ID].Y,
3154 AreaSize[gAreas[ID].AreaType].Width,
3155 AreaSize[gAreas[ID].AreaType].Height);
3156 j_max := Length(gAreas) - 1;
3157 end;
3159 OBJECT_TRIGGER:
3160 begin
3161 res := (gTriggers <> nil) and
3162 LayerEnabled[LAYER_TRIGGERS] and
3163 g_CollidePoint(X, Y, gTriggers[ID].X, gTriggers[ID].Y,
3164 gTriggers[ID].Width,
3165 gTriggers[ID].Height);
3166 j_max := Length(gTriggers) - 1;
3167 end;
3169 else
3170 res := False;
3171 end;
3173 if not res then
3174 Exit;
3176 // Перебор ID: от ID-1 до 0; потом от High до ID+1:
3177 j := ID;
3179 while True do
3180 begin
3181 Dec(j);
3183 if j < 0 then
3184 j := j_max;
3185 if j = Integer(ID) then
3186 Break;
3188 case ObjectType of
3189 OBJECT_PANEL:
3190 res := PanelInShownLayer(gPanels[j].PanelType) and
3191 g_CollidePoint(X, Y, gPanels[j].X, gPanels[j].Y,
3192 gPanels[j].Width,
3193 gPanels[j].Height);
3194 OBJECT_ITEM:
3195 res := (gItems[j].ItemType <> ITEM_NONE) and
3196 g_CollidePoint(X, Y, gItems[j].X, gItems[j].Y,
3197 ItemSize[gItems[j].ItemType][0],
3198 ItemSize[gItems[j].ItemType][1]);
3199 OBJECT_MONSTER:
3200 res := (gMonsters[j].MonsterType <> MONSTER_NONE) and
3201 g_CollidePoint(X, Y, gMonsters[j].X, gMonsters[j].Y,
3202 MonsterSize[gMonsters[j].MonsterType].Width,
3203 MonsterSize[gMonsters[j].MonsterType].Height);
3204 OBJECT_AREA:
3205 res := (gAreas[j].AreaType <> AREA_NONE) and
3206 g_CollidePoint(X, Y, gAreas[j].X, gAreas[j].Y,
3207 AreaSize[gAreas[j].AreaType].Width,
3208 AreaSize[gAreas[j].AreaType].Height);
3209 OBJECT_TRIGGER:
3210 res := (gTriggers[j].TriggerType <> TRIGGER_NONE) and
3211 g_CollidePoint(X, Y, gTriggers[j].X, gTriggers[j].Y,
3212 gTriggers[j].Width,
3213 gTriggers[j].Height);
3214 else
3215 res := False;
3216 end;
3218 if res then
3219 begin
3220 SetLength(SelectedObjects, 1);
3222 SelectedObjects[0].ObjectType := ObjectType;
3223 SelectedObjects[0].ID := j;
3224 SelectedObjects[0].Live := True;
3226 FillProperty();
3227 Break;
3228 end;
3229 end;
3230 end;
3232 procedure TMainForm.RenderPanelMouseDown(Sender: TObject;
3233 Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
3234 var
3235 i: Integer;
3236 Rect: TRectWH;
3237 c1, c2, c3, c4: Boolean;
3238 item: TItem;
3239 area: TArea;
3240 monster: TMonster;
3241 IDArray: DWArray;
3242 begin
3243 MainForm.ActiveControl := RenderPanel;
3244 RenderPanel.SetFocus();
3246 RenderPanelMouseMove(RenderPanel, Shift, X, Y);
3248 if Button = mbLeft then // Left Mouse Button
3249 begin
3250 // Двигаем карту с помощью мыши и мини-карты:
3251 if ShowMap and
3252 g_CollidePoint(X, Y,
3253 RenderPanel.Width-max(gMapInfo.Width div (16 div Scale), 1)-1,
3254 1,
3255 max(gMapInfo.Width div (16 div Scale), 1),
3256 max(gMapInfo.Height div (16 div Scale), 1) ) then
3257 begin
3258 MoveMap(X, Y);
3259 MouseAction := MOUSEACTION_MOVEMAP;
3260 end
3261 else // Ставим предмет/монстра/область:
3262 if (pcObjects.ActivePageIndex in [1, 2, 3]) and
3263 (not (ssShift in Shift)) then
3264 begin
3265 case pcObjects.ActivePageIndex of
3266 1:
3267 if lbItemList.ItemIndex = -1 then
3268 ErrorMessageBox(_lc[I_MSG_CHOOSE_ITEM])
3269 else
3270 begin
3271 item.ItemType := lbItemList.ItemIndex + ITEM_MEDKIT_SMALL;
3272 if item.ItemType >= ITEM_WEAPON_KASTET then
3273 item.ItemType := item.ItemType + 2;
3274 item.X := MousePos.X-MapOffset.X;
3275 item.Y := MousePos.Y-MapOffset.Y;
3277 if not (ssCtrl in Shift) then
3278 begin
3279 item.X := item.X - (ItemSize[item.ItemType][0] div 2);
3280 item.Y := item.Y - ItemSize[item.ItemType][1];
3281 end;
3283 item.OnlyDM := cbOnlyDM.Checked;
3284 item.Fall := cbFall.Checked;
3285 Undo_Add(OBJECT_ITEM, AddItem(item));
3286 end;
3287 2:
3288 if lbMonsterList.ItemIndex = -1 then
3289 ErrorMessageBox(_lc[I_MSG_CHOOSE_MONSTER])
3290 else
3291 begin
3292 monster.MonsterType := lbMonsterList.ItemIndex + MONSTER_DEMON;
3293 monster.X := MousePos.X-MapOffset.X;
3294 monster.Y := MousePos.Y-MapOffset.Y;
3296 if not (ssCtrl in Shift) then
3297 begin
3298 monster.X := monster.X - (MonsterSize[monster.MonsterType].Width div 2);
3299 monster.Y := monster.Y - MonsterSize[monster.MonsterType].Height;
3300 end;
3302 if rbMonsterLeft.Checked then
3303 monster.Direction := D_LEFT
3304 else
3305 monster.Direction := D_RIGHT;
3306 Undo_Add(OBJECT_MONSTER, AddMonster(monster));
3307 end;
3308 3:
3309 if lbAreasList.ItemIndex = -1 then
3310 ErrorMessageBox(_lc[I_MSG_CHOOSE_AREA])
3311 else
3312 if (lbAreasList.ItemIndex + 1) <> AREA_DOMFLAG then
3313 begin
3314 area.AreaType := lbAreasList.ItemIndex + AREA_PLAYERPOINT1;
3315 area.X := MousePos.X-MapOffset.X;
3316 area.Y := MousePos.Y-MapOffset.Y;
3318 if not (ssCtrl in Shift) then
3319 begin
3320 area.X := area.X - (AreaSize[area.AreaType].Width div 2);
3321 area.Y := area.Y - AreaSize[area.AreaType].Height;
3322 end;
3324 if rbAreaLeft.Checked then
3325 area.Direction := D_LEFT
3326 else
3327 area.Direction := D_RIGHT;
3328 Undo_Add(OBJECT_AREA, AddArea(area));
3329 end;
3330 end;
3331 end
3332 else
3333 begin
3334 i := GetFirstSelected();
3336 // Выбираем объект под текущим:
3337 if (SelectedObjects <> nil) and
3338 (ssShift in Shift) and (i >= 0) and
3339 (SelectedObjects[i].Live) then
3340 begin
3341 if SelectedObjectCount() = 1 then
3342 SelectNextObject(X-MapOffset.X, Y-MapOffset.Y,
3343 SelectedObjects[i].ObjectType,
3344 SelectedObjects[i].ID);
3345 end
3346 else
3347 begin
3348 // Рисуем область триггера "Расширитель":
3349 if DrawPressRect and (i >= 0) and
3350 (SelectedObjects[i].ObjectType = OBJECT_TRIGGER) and
3351 (gTriggers[SelectedObjects[i].ID].TriggerType in
3352 [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF]) then
3353 MouseAction := MOUSEACTION_DRAWPRESS
3354 else // Рисуем панель:
3355 if pcObjects.ActivePageIndex = 0 then
3356 begin
3357 if (lbPanelType.ItemIndex >= 0) then
3358 MouseAction := MOUSEACTION_DRAWPANEL
3359 end
3360 else // Рисуем триггер:
3361 if (lbTriggersList.ItemIndex >= 0) then
3362 begin
3363 MouseAction := MOUSEACTION_DRAWTRIGGER;
3364 end;
3365 end;
3366 end;
3367 end; // if Button = mbLeft
3369 if Button = mbRight then // Right Mouse Button
3370 begin
3371 // Клик по мини-карте:
3372 if ShowMap and
3373 g_CollidePoint(X, Y,
3374 RenderPanel.Width-max(gMapInfo.Width div (16 div Scale), 1)-1,
3375 1,
3376 max(gMapInfo.Width div (16 div Scale), 1),
3377 max(gMapInfo.Height div (16 div Scale), 1) ) then
3378 begin
3379 MouseAction := MOUSEACTION_NOACTION;
3380 end
3381 else // Нужно что-то выбрать мышью:
3382 if SelectFlag <> SELECTFLAG_NONE then
3383 begin
3384 case SelectFlag of
3385 SELECTFLAG_TELEPORT:
3386 // Точку назначения телепортации:
3387 with gTriggers[SelectedObjects[
3388 GetFirstSelected() ].ID].Data.TargetPoint do
3389 begin
3390 X := MousePos.X-MapOffset.X;
3391 Y := MousePos.Y-MapOffset.Y;
3392 end;
3394 SELECTFLAG_SPAWNPOINT:
3395 // Точку создания монстра:
3396 with gTriggers[SelectedObjects[GetFirstSelected()].ID] do
3397 if TriggerType = TRIGGER_SPAWNMONSTER then
3398 begin
3399 Data.MonPos.X := MousePos.X-MapOffset.X;
3400 Data.MonPos.Y := MousePos.Y-MapOffset.Y;
3401 end
3402 else if TriggerType = TRIGGER_SPAWNITEM then
3403 begin // Точка создания предмета:
3404 Data.ItemPos.X := MousePos.X-MapOffset.X;
3405 Data.ItemPos.Y := MousePos.Y-MapOffset.Y;
3406 end
3407 else if TriggerType = TRIGGER_SHOT then
3408 begin // Точка создания выстрела:
3409 Data.ShotPos.X := MousePos.X-MapOffset.X;
3410 Data.ShotPos.Y := MousePos.Y-MapOffset.Y;
3411 end;
3413 SELECTFLAG_DOOR:
3414 // Дверь:
3415 begin
3416 IDArray := ObjectInRect(X-MapOffset.X,
3417 Y-MapOffset.Y,
3418 2, 2, OBJECT_PANEL, True);
3419 if IDArray <> nil then
3420 begin
3421 for i := 0 to High(IDArray) do
3422 if (gPanels[IDArray[i]].PanelType = PANEL_OPENDOOR) or
3423 (gPanels[IDArray[i]].PanelType = PANEL_CLOSEDOOR) then
3424 begin
3425 gTriggers[SelectedObjects[
3426 GetFirstSelected() ].ID].Data.PanelID := IDArray[i];
3427 Break;
3428 end;
3429 end
3430 else
3431 gTriggers[SelectedObjects[
3432 GetFirstSelected() ].ID].Data.PanelID := -1;
3433 end;
3435 SELECTFLAG_TEXTURE:
3436 // Панель с текстурой:
3437 begin
3438 IDArray := ObjectInRect(X-MapOffset.X,
3439 Y-MapOffset.Y,
3440 2, 2, OBJECT_PANEL, True);
3441 if IDArray <> nil then
3442 begin
3443 for i := 0 to High(IDArray) do
3444 if ((gPanels[IDArray[i]].PanelType in
3445 [PANEL_WALL, PANEL_BACK, PANEL_FORE,
3446 PANEL_WATER, PANEL_ACID1, PANEL_ACID2,
3447 PANEL_STEP]) or
3448 (gPanels[IDArray[i]].PanelType = PANEL_OPENDOOR) or
3449 (gPanels[IDArray[i]].PanelType = PANEL_CLOSEDOOR)) and
3450 (gPanels[IDArray[i]].TextureName <> '') then
3451 begin
3452 gTriggers[SelectedObjects[
3453 GetFirstSelected() ].ID].TexturePanel := IDArray[i];
3454 Break;
3455 end;
3456 end
3457 else
3458 gTriggers[SelectedObjects[
3459 GetFirstSelected() ].ID].TexturePanel := -1;
3460 end;
3462 SELECTFLAG_LIFT:
3463 // Лифт:
3464 begin
3465 IDArray := ObjectInRect(X-MapOffset.X,
3466 Y-MapOffset.Y,
3467 2, 2, OBJECT_PANEL, True);
3468 if IDArray <> nil then
3469 begin
3470 for i := 0 to High(IDArray) do
3471 if (gPanels[IDArray[i]].PanelType = PANEL_LIFTUP) or
3472 (gPanels[IDArray[i]].PanelType = PANEL_LIFTDOWN) or
3473 (gPanels[IDArray[i]].PanelType = PANEL_LIFTLEFT) or
3474 (gPanels[IDArray[i]].PanelType = PANEL_LIFTRIGHT) then
3475 begin
3476 gTriggers[SelectedObjects[
3477 GetFirstSelected() ].ID].Data.PanelID := IDArray[i];
3478 Break;
3479 end;
3480 end
3481 else
3482 gTriggers[SelectedObjects[
3483 GetFirstSelected() ].ID].Data.PanelID := -1;
3484 end;
3486 SELECTFLAG_MONSTER:
3487 // Монстра:
3488 begin
3489 IDArray := ObjectInRect(X-MapOffset.X,
3490 Y-MapOffset.Y,
3491 2, 2, OBJECT_MONSTER, False);
3492 if IDArray <> nil then
3493 gTriggers[SelectedObjects[
3494 GetFirstSelected() ].ID].Data.MonsterID := IDArray[0]+1
3495 else
3496 gTriggers[SelectedObjects[
3497 GetFirstSelected() ].ID].Data.MonsterID := 0;
3498 end;
3500 SELECTFLAG_SHOTPANEL:
3501 // Панель индикации выстрела:
3502 begin
3503 if gTriggers[SelectedObjects[
3504 GetFirstSelected() ].ID].TriggerType = TRIGGER_SHOT then
3505 begin
3506 IDArray := ObjectInRect(X-MapOffset.X,
3507 Y-MapOffset.Y,
3508 2, 2, OBJECT_PANEL, True);
3509 if IDArray <> nil then
3510 begin
3511 for i := 0 to High(IDArray) do
3512 if ((gPanels[IDArray[i]].PanelType in
3513 [PANEL_WALL, PANEL_BACK, PANEL_FORE,
3514 PANEL_WATER, PANEL_ACID1, PANEL_ACID2,
3515 PANEL_STEP]) or
3516 (gPanels[IDArray[i]].PanelType = PANEL_OPENDOOR) or
3517 (gPanels[IDArray[i]].PanelType = PANEL_CLOSEDOOR)) and
3518 (gPanels[IDArray[i]].TextureName <> '') then
3519 begin
3520 gTriggers[SelectedObjects[
3521 GetFirstSelected() ].ID].Data.ShotPanelID := IDArray[i];
3522 Break;
3523 end;
3524 end
3525 else
3526 gTriggers[SelectedObjects[
3527 GetFirstSelected() ].ID].Data.ShotPanelID := -1;
3528 end;
3529 end;
3530 end;
3532 SelectFlag := SELECTFLAG_SELECTED;
3533 end
3534 else // if SelectFlag <> SELECTFLAG_NONE...
3535 begin
3536 // Что уже выбрано и не нажат Ctrl:
3537 if (SelectedObjects <> nil) and
3538 (not (ssCtrl in Shift)) then
3539 for i := 0 to High(SelectedObjects) do
3540 with SelectedObjects[i] do
3541 if Live then
3542 begin
3543 if (ObjectType in [OBJECT_PANEL, OBJECT_TRIGGER]) and
3544 (SelectedObjectCount() = 1) then
3545 begin
3546 Rect := ObjectGetRect(ObjectType, ID);
3548 c1 := g_Collide(X-MapOffset.X-1, Y-MapOffset.Y-1, 2, 2,
3549 Rect.X-2, Rect.Y+(Rect.Height div 2)-2, 4, 4);
3550 c2 := g_Collide(X-MapOffset.X-1, Y-MapOffset.Y-1, 2, 2,
3551 Rect.X+Rect.Width-3, Rect.Y+(Rect.Height div 2)-2, 4, 4);
3552 c3 := g_Collide(X-MapOffset.X-1, Y-MapOffset.Y-1, 2, 2,
3553 Rect.X+(Rect.Width div 2)-2, Rect.Y-2, 4, 4);
3554 c4 := g_Collide(X-MapOffset.X-1, Y-MapOffset.Y-1, 2, 2,
3555 Rect.X+(Rect.Width div 2)-2, Rect.Y+Rect.Height-3, 4, 4);
3557 // Меняем размер панели или триггера:
3558 if c1 or c2 or c3 or c4 then
3559 begin
3560 MouseAction := MOUSEACTION_RESIZE;
3561 LastMovePoint := MousePos;
3563 if c1 or c2 then
3564 begin // Шире/уже
3565 ResizeType := RESIZETYPE_HORIZONTAL;
3566 if c1 then
3567 ResizeDirection := RESIZEDIR_LEFT
3568 else
3569 ResizeDirection := RESIZEDIR_RIGHT;
3570 RenderPanel.Cursor := crSizeWE;
3571 end
3572 else
3573 begin // Выше/ниже
3574 ResizeType := RESIZETYPE_VERTICAL;
3575 if c3 then
3576 ResizeDirection := RESIZEDIR_UP
3577 else
3578 ResizeDirection := RESIZEDIR_DOWN;
3579 RenderPanel.Cursor := crSizeNS;
3580 end;
3582 Break;
3583 end;
3584 end;
3586 // Перемещаем панель или триггер:
3587 if ObjectCollide(ObjectType, ID,
3588 X-MapOffset.X-1,
3589 Y-MapOffset.Y-1, 2, 2) then
3590 begin
3591 MouseAction := MOUSEACTION_MOVEOBJ;
3592 LastMovePoint := MousePos;
3594 Break;
3595 end;
3596 end;
3597 end;
3598 end; // if Button = mbRight
3600 if Button = mbMiddle then // Middle Mouse Button
3601 begin
3602 SetCapture(RenderPanel.Handle);
3603 RenderPanel.Cursor := crSize;
3604 end;
3606 MouseMDown := Button = mbMiddle;
3607 if MouseMDown then
3608 MouseMDownPos := Mouse.CursorPos;
3610 MouseRDown := Button = mbRight;
3611 if MouseRDown then
3612 MouseRDownPos := MousePos;
3614 MouseLDown := Button = mbLeft;
3615 if MouseLDown then
3616 MouseLDownPos := MousePos;
3617 end;
3619 procedure TMainForm.RenderPanelMouseUp(Sender: TObject;
3620 Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
3621 var
3622 panel: TPanel;
3623 trigger: TTrigger;
3624 rRect: TRectWH;
3625 rSelectRect: Boolean;
3626 wWidth, wHeight: Word;
3627 TextureID: DWORD;
3629 procedure SelectObjects(ObjectType: Byte);
3630 var
3631 i: Integer;
3632 IDArray: DWArray;
3633 begin
3634 IDArray := ObjectInRect(rRect.X, rRect.Y,
3635 rRect.Width, rRect.Height,
3636 ObjectType, rSelectRect);
3638 if IDArray <> nil then
3639 for i := 0 to High(IDArray) do
3640 SelectObject(ObjectType, IDArray[i], (ssCtrl in Shift) or rSelectRect);
3641 end;
3642 begin
3643 if Button = mbLeft then
3644 MouseLDown := False;
3645 if Button = mbRight then
3646 MouseRDown := False;
3647 if Button = mbMiddle then
3648 MouseMDown := False;
3650 DrawRect := nil;
3651 ResizeType := RESIZETYPE_NONE;
3652 TextureID := 0;
3654 if Button = mbLeft then // Left Mouse Button
3655 begin
3656 if MouseAction <> MOUSEACTION_NONE then
3657 begin // Было действие мышью
3658 // Мышь сдвинулась во время удержания клавиши,
3659 // либо активирован режим быстрого рисования:
3660 if ((MousePos.X <> MouseLDownPos.X) and
3661 (MousePos.Y <> MouseLDownPos.Y)) or
3662 ((MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER]) and
3663 (ssCtrl in Shift)) then
3664 case MouseAction of
3665 // Рисовали панель:
3666 MOUSEACTION_DRAWPANEL:
3667 begin
3668 // Фон или передний план без текстуры - ошибка:
3669 if (lbPanelType.ItemIndex in [1, 2]) and
3670 (lbTextureList.ItemIndex = -1) then
3671 ErrorMessageBox(_lc[I_MSG_CHOOSE_TEXTURE])
3672 else // Назначаем параметры панели:
3673 begin
3674 case lbPanelType.ItemIndex of
3675 0: Panel.PanelType := PANEL_WALL;
3676 1: Panel.PanelType := PANEL_BACK;
3677 2: Panel.PanelType := PANEL_FORE;
3678 3: Panel.PanelType := PANEL_OPENDOOR;
3679 4: Panel.PanelType := PANEL_CLOSEDOOR;
3680 5: Panel.PanelType := PANEL_STEP;
3681 6: Panel.PanelType := PANEL_WATER;
3682 7: Panel.PanelType := PANEL_ACID1;
3683 8: Panel.PanelType := PANEL_ACID2;
3684 9: Panel.PanelType := PANEL_LIFTUP;
3685 10: Panel.PanelType := PANEL_LIFTDOWN;
3686 11: Panel.PanelType := PANEL_LIFTLEFT;
3687 12: Panel.PanelType := PANEL_LIFTRIGHT;
3688 13: Panel.PanelType := PANEL_BLOCKMON;
3689 end;
3691 Panel.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
3692 Panel.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
3693 if ssCtrl in Shift then
3694 begin
3695 wWidth := DotStep;
3696 wHeight := DotStep;
3697 if (lbTextureList.ItemIndex <> -1) and
3698 (not IsSpecialTextureSel()) then
3699 begin
3700 if not g_GetTexture(SelectedTexture(), TextureID) then
3701 g_GetTexture('NOTEXTURE', TextureID);
3702 g_GetTextureSizeByID(TextureID, wWidth, wHeight);
3703 end;
3704 Panel.Width := wWidth;
3705 Panel.Height := wHeight;
3706 end
3707 else
3708 begin
3709 Panel.Width := Abs(MousePos.X-MouseLDownPos.X);
3710 Panel.Height := Abs(MousePos.Y-MouseLDownPos.Y);
3711 end;
3713 // Лифты, блокМон или отсутствие текстуры - пустая текстура:
3714 if (lbPanelType.ItemIndex in [9, 10, 11, 12, 13]) or
3715 (lbTextureList.ItemIndex = -1) then
3716 begin
3717 Panel.TextureHeight := 1;
3718 Panel.TextureWidth := 1;
3719 Panel.TextureName := '';
3720 Panel.TextureID := TEXTURE_SPECIAL_NONE;
3721 end
3722 else // Есть текстура:
3723 begin
3724 Panel.TextureName := SelectedTexture();
3726 // Обычная текстура:
3727 if not IsSpecialTextureSel() then
3728 begin
3729 g_GetTextureSizeByName(Panel.TextureName,
3730 Panel.TextureWidth, Panel.TextureHeight);
3731 g_GetTexture(Panel.TextureName, Panel.TextureID);
3732 end
3733 else // Спец.текстура:
3734 begin
3735 Panel.TextureHeight := 1;
3736 Panel.TextureWidth := 1;
3737 Panel.TextureID := SpecialTextureID(SelectedTexture());
3738 end;
3739 end;
3741 Panel.Alpha := 0;
3742 Panel.Blending := False;
3744 Undo_Add(OBJECT_PANEL, AddPanel(Panel));
3745 end;
3746 end;
3748 // Рисовали триггер:
3749 MOUSEACTION_DRAWTRIGGER:
3750 begin
3751 trigger.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
3752 trigger.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
3753 if ssCtrl in Shift then
3754 begin
3755 wWidth := DotStep;
3756 wHeight := DotStep;
3757 trigger.Width := wWidth;
3758 trigger.Height := wHeight;
3759 end
3760 else
3761 begin
3762 trigger.Width := Abs(MousePos.X-MouseLDownPos.X);
3763 trigger.Height := Abs(MousePos.Y-MouseLDownPos.Y);
3764 end;
3766 trigger.Enabled := True;
3767 trigger.TriggerType := lbTriggersList.ItemIndex+1;
3768 trigger.TexturePanel := -1;
3770 // Типы активации:
3771 trigger.ActivateType := 0;
3773 if clbActivationType.Checked[0] then
3774 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_PLAYERCOLLIDE;
3775 if clbActivationType.Checked[1] then
3776 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_MONSTERCOLLIDE;
3777 if clbActivationType.Checked[2] then
3778 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_PLAYERPRESS;
3779 if clbActivationType.Checked[3] then
3780 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_MONSTERPRESS;
3781 if clbActivationType.Checked[4] then
3782 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_SHOT;
3783 if clbActivationType.Checked[5] then
3784 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_NOMONSTER;
3786 // Необходимые для активации ключи:
3787 trigger.Key := 0;
3789 if clbKeys.Checked[0] then
3790 trigger.Key := Trigger.Key or KEY_RED;
3791 if clbKeys.Checked[1] then
3792 trigger.Key := Trigger.Key or KEY_GREEN;
3793 if clbKeys.Checked[2] then
3794 trigger.Key := Trigger.Key or KEY_BLUE;
3795 if clbKeys.Checked[3] then
3796 trigger.Key := Trigger.Key or KEY_REDTEAM;
3797 if clbKeys.Checked[4] then
3798 trigger.Key := Trigger.Key or KEY_BLUETEAM;
3800 // Параметры триггера:
3801 FillByte(trigger.Data.Default[0], 128, 0);
3803 case trigger.TriggerType of
3804 // Переключаемая панель:
3805 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
3806 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
3807 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
3808 begin
3809 Trigger.Data.PanelID := -1;
3810 end;
3812 // Телепортация:
3813 TRIGGER_TELEPORT:
3814 begin
3815 trigger.Data.TargetPoint.X := trigger.X-64;
3816 trigger.Data.TargetPoint.Y := trigger.Y-64;
3817 trigger.Data.d2d_teleport := True;
3818 trigger.Data.TlpDir := 0;
3819 end;
3821 // Изменение других триггеров:
3822 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF,
3823 TRIGGER_ONOFF:
3824 begin
3825 trigger.Data.Count := 1;
3826 end;
3828 // Звук:
3829 TRIGGER_SOUND:
3830 begin
3831 trigger.Data.Volume := 255;
3832 trigger.Data.Pan := 127;
3833 trigger.Data.PlayCount := 1;
3834 trigger.Data.Local := True;
3835 trigger.Data.SoundSwitch := False;
3836 end;
3838 // Музыка:
3839 TRIGGER_MUSIC:
3840 begin
3841 trigger.Data.MusicAction := 1;
3842 end;
3844 // Создание монстра:
3845 TRIGGER_SPAWNMONSTER:
3846 begin
3847 trigger.Data.MonType := MONSTER_ZOMBY;
3848 trigger.Data.MonPos.X := trigger.X-64;
3849 trigger.Data.MonPos.Y := trigger.Y-64;
3850 trigger.Data.MonHealth := 0;
3851 trigger.Data.MonActive := False;
3852 trigger.Data.MonCount := 1;
3853 end;
3855 // Создание предмета:
3856 TRIGGER_SPAWNITEM:
3857 begin
3858 trigger.Data.ItemType := ITEM_AMMO_BULLETS;
3859 trigger.Data.ItemPos.X := trigger.X-64;
3860 trigger.Data.ItemPos.Y := trigger.Y-64;
3861 trigger.Data.ItemOnlyDM := False;
3862 trigger.Data.ItemFalls := False;
3863 trigger.Data.ItemCount := 1;
3864 trigger.Data.ItemMax := 0;
3865 trigger.Data.ItemDelay := 0;
3866 end;
3868 // Ускорение:
3869 TRIGGER_PUSH:
3870 begin
3871 trigger.Data.PushAngle := 90;
3872 trigger.Data.PushForce := 10;
3873 trigger.Data.ResetVel := True;
3874 end;
3876 TRIGGER_SCORE:
3877 begin
3878 trigger.Data.ScoreCount := 1;
3879 trigger.Data.ScoreCon := True;
3880 trigger.Data.ScoreMsg := True;
3881 end;
3883 TRIGGER_MESSAGE:
3884 begin
3885 trigger.Data.MessageKind := 0;
3886 trigger.Data.MessageSendTo := 0;
3887 trigger.Data.MessageText := '';
3888 trigger.Data.MessageTime := 144;
3889 end;
3891 TRIGGER_DAMAGE:
3892 begin
3893 trigger.Data.DamageValue := 5;
3894 trigger.Data.DamageInterval := 12;
3895 end;
3897 TRIGGER_HEALTH:
3898 begin
3899 trigger.Data.HealValue := 5;
3900 trigger.Data.HealInterval := 36;
3901 end;
3903 TRIGGER_SHOT:
3904 begin
3905 trigger.Data.ShotType := TRIGGER_SHOT_BULLET;
3906 trigger.Data.ShotSound := True;
3907 trigger.Data.ShotPanelID := -1;
3908 trigger.Data.ShotTarget := 0;
3909 trigger.Data.ShotIntSight := 0;
3910 trigger.Data.ShotAim := TRIGGER_SHOT_AIM_DEFAULT;
3911 trigger.Data.ShotPos.X := trigger.X-64;
3912 trigger.Data.ShotPos.Y := trigger.Y-64;
3913 trigger.Data.ShotAngle := 0;
3914 trigger.Data.ShotWait := 18;
3915 trigger.Data.ShotAccuracy := 0;
3916 trigger.Data.ShotAmmo := 0;
3917 trigger.Data.ShotIntReload := 0;
3918 end;
3920 TRIGGER_EFFECT:
3921 begin
3922 trigger.Data.FXCount := 1;
3923 trigger.Data.FXType := TRIGGER_EFFECT_PARTICLE;
3924 trigger.Data.FXSubType := TRIGGER_EFFECT_SLIQUID;
3925 trigger.Data.FXColorR := 0;
3926 trigger.Data.FXColorG := 0;
3927 trigger.Data.FXColorB := 255;
3928 trigger.Data.FXPos := TRIGGER_EFFECT_POS_CENTER;
3929 trigger.Data.FXWait := 1;
3930 trigger.Data.FXVelX := 0;
3931 trigger.Data.FXVelY := -20;
3932 trigger.Data.FXSpreadL := 5;
3933 trigger.Data.FXSpreadR := 5;
3934 trigger.Data.FXSpreadU := 4;
3935 trigger.Data.FXSpreadD := 0;
3936 end;
3937 end;
3939 Undo_Add(OBJECT_TRIGGER, AddTrigger(trigger));
3940 end;
3942 // Рисовали область триггера "Расширитель":
3943 MOUSEACTION_DRAWPRESS:
3944 with gTriggers[SelectedObjects[GetFirstSelected].ID] do
3945 begin
3946 Data.tX := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
3947 Data.tY := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
3948 Data.tWidth := Abs(MousePos.X-MouseLDownPos.X);
3949 Data.tHeight := Abs(MousePos.Y-MouseLDownPos.Y);
3951 DrawPressRect := False;
3952 end;
3953 end;
3955 MouseAction := MOUSEACTION_NONE;
3956 end;
3957 end // if Button = mbLeft...
3958 else if Button = mbRight then // Right Mouse Button:
3959 begin
3960 if MouseAction = MOUSEACTION_NOACTION then
3961 begin
3962 MouseAction := MOUSEACTION_NONE;
3963 Exit;
3964 end;
3966 // Объект передвинут или изменен в размере:
3967 if MouseAction in [MOUSEACTION_MOVEOBJ, MOUSEACTION_RESIZE] then
3968 begin
3969 RenderPanel.Cursor := crDefault;
3970 MouseAction := MOUSEACTION_NONE;
3971 FillProperty();
3972 Exit;
3973 end;
3975 // Еще не все выбрали:
3976 if SelectFlag <> SELECTFLAG_NONE then
3977 begin
3978 if SelectFlag = SELECTFLAG_SELECTED then
3979 SelectFlag := SELECTFLAG_NONE;
3980 FillProperty();
3981 Exit;
3982 end;
3984 // Мышь сдвинулась во время удержания клавиши:
3985 if (MousePos.X <> MouseRDownPos.X) and
3986 (MousePos.Y <> MouseRDownPos.Y) then
3987 begin
3988 rSelectRect := True;
3990 rRect.X := Min(MousePos.X, MouseRDownPos.X)-MapOffset.X;
3991 rRect.Y := Min(MousePos.Y, MouseRDownPos.Y)-MapOffset.Y;
3992 rRect.Width := Abs(MousePos.X-MouseRDownPos.X);
3993 rRect.Height := Abs(MousePos.Y-MouseRDownPos.Y);
3994 end
3995 else // Мышь не сдвинулась - нет прямоугольника:
3996 begin
3997 rSelectRect := False;
3999 rRect.X := X-MapOffset.X-1;
4000 rRect.Y := Y-MapOffset.Y-1;
4001 rRect.Width := 2;
4002 rRect.Height := 2;
4003 end;
4005 // Если зажат Ctrl - выделять еще, иначе только один выделенный объект:
4006 if not (ssCtrl in Shift) then
4007 RemoveSelectFromObjects();
4009 // Выделяем всё в выбранном прямоугольнике:
4010 if (ssCtrl in Shift) and (ssAlt in Shift) then
4011 begin
4012 SelectObjects(OBJECT_PANEL);
4013 SelectObjects(OBJECT_ITEM);
4014 SelectObjects(OBJECT_MONSTER);
4015 SelectObjects(OBJECT_AREA);
4016 SelectObjects(OBJECT_TRIGGER);
4017 end
4018 else
4019 SelectObjects(pcObjects.ActivePageIndex+1);
4021 FillProperty();
4022 end
4024 else // Middle Mouse Button
4025 begin
4026 RenderPanel.Cursor := crDefault;
4027 ReleaseCapture();
4028 end;
4029 end;
4031 procedure TMainForm.RenderPanelPaint(Sender: TObject);
4032 begin
4033 Draw();
4034 end;
4036 function TMainForm.RenderMousePos(): Types.TPoint;
4037 begin
4038 Result := RenderPanel.ScreenToClient(Mouse.CursorPos);
4039 end;
4041 procedure TMainForm.RecountSelectedObjects();
4042 begin
4043 if SelectedObjectCount() = 0 then
4044 StatusBar.Panels[0].Text := ''
4045 else
4046 StatusBar.Panels[0].Text := Format(_lc[I_CAP_STAT_SELECTED], [SelectedObjectCount()]);
4047 end;
4049 procedure TMainForm.RenderPanelMouseMove(Sender: TObject;
4050 Shift: TShiftState; X, Y: Integer);
4051 var
4052 sX, sY: Integer;
4053 dWidth, dHeight: Integer;
4054 _id: Integer;
4055 TextureID: DWORD;
4056 wWidth, wHeight: Word;
4057 begin
4058 _id := GetFirstSelected();
4059 TextureID := 0;
4061 // Рисуем панель с текстурой, сетка - размеры текстуры:
4062 if (MouseAction = MOUSEACTION_DRAWPANEL) and
4063 (lbPanelType.ItemIndex in [0..8]) and
4064 (lbTextureList.ItemIndex <> -1) and
4065 (not IsSpecialTextureSel()) then
4066 begin
4067 sX := StrToIntDef(lTextureWidth.Caption, DotStep);
4068 sY := StrToIntDef(lTextureHeight.Caption, DotStep);
4069 end
4070 else
4071 // Меняем размер панели с текстурой, сетка - размеры текстуры:
4072 if (MouseAction = MOUSEACTION_RESIZE) and
4073 ( (SelectedObjects[_id].ObjectType = OBJECT_PANEL) and
4074 IsTexturedPanel(gPanels[SelectedObjects[_id].ID].PanelType) and
4075 (gPanels[SelectedObjects[_id].ID].TextureName <> '') and
4076 (not IsSpecialTexture(gPanels[SelectedObjects[_id].ID].TextureName)) ) then
4077 begin
4078 sX := gPanels[SelectedObjects[_id].ID].TextureWidth;
4079 sY := gPanels[SelectedObjects[_id].ID].TextureHeight;
4080 end
4081 else
4082 // Выравнивание по сетке:
4083 if SnapToGrid then
4084 begin
4085 sX := DotStep;
4086 sY := DotStep;
4087 end
4088 else // Нет выравнивания по сетке:
4089 begin
4090 sX := 1;
4091 sY := 1;
4092 end;
4094 // Новая позиция мыши:
4095 if MouseLDown then
4096 begin // Зажата левая кнопка мыши
4097 MousePos.X := (Round((X-MouseLDownPos.X)/sX)*sX)+MouseLDownPos.X;
4098 MousePos.Y := (Round((Y-MouseLDownPos.Y)/sY)*sY)+MouseLDownPos.Y;
4099 end
4100 else
4101 if MouseRDown then
4102 begin // Зажата правая кнопка мыши
4103 MousePos.X := (Round((X-MouseRDownPos.X)/sX)*sX)+MouseRDownPos.X;
4104 MousePos.Y := (Round((Y-MouseRDownPos.Y)/sY)*sY)+MouseRDownPos.Y;
4105 end
4106 else
4107 begin // Кнопки мыши не зажаты
4108 MousePos.X := Round((-MapOffset.X + X) / sX) * sX + MapOffset.X;
4109 MousePos.Y := Round((-MapOffset.Y + Y) / sY) * sY + MapOffset.Y;
4110 end;
4112 // Зажата только правая кнопка мыши:
4113 if (not MouseLDown) and (MouseRDown) and (not MouseMDown) then
4114 begin
4115 // Рисуем прямоугольник выделения:
4116 if MouseAction = MOUSEACTION_NONE then
4117 begin
4118 if DrawRect = nil then
4119 New(DrawRect);
4120 DrawRect.Top := MouseRDownPos.y;
4121 DrawRect.Left := MouseRDownPos.x;
4122 DrawRect.Bottom := MousePos.y;
4123 DrawRect.Right := MousePos.x;
4124 end
4125 else
4126 // Двигаем выделенные объекты:
4127 if MouseAction = MOUSEACTION_MOVEOBJ then
4128 begin
4129 MoveSelectedObjects(ssShift in Shift, ssCtrl in Shift,
4130 MousePos.X-LastMovePoint.X,
4131 MousePos.Y-LastMovePoint.Y);
4132 end
4133 else
4134 // Меняем размер выделенного объекта:
4135 if MouseAction = MOUSEACTION_RESIZE then
4136 begin
4137 if (SelectedObjectCount = 1) and
4138 (SelectedObjects[GetFirstSelected].Live) then
4139 begin
4140 dWidth := MousePos.X-LastMovePoint.X;
4141 dHeight := MousePos.Y-LastMovePoint.Y;
4143 case ResizeType of
4144 RESIZETYPE_VERTICAL: dWidth := 0;
4145 RESIZETYPE_HORIZONTAL: dHeight := 0;
4146 end;
4148 case ResizeDirection of
4149 RESIZEDIR_UP: dHeight := -dHeight;
4150 RESIZEDIR_LEFT: dWidth := -dWidth;
4151 end;
4153 if ResizeObject(SelectedObjects[GetFirstSelected].ObjectType,
4154 SelectedObjects[GetFirstSelected].ID,
4155 dWidth, dHeight, ResizeDirection) then
4156 LastMovePoint := MousePos;
4157 end;
4158 end;
4159 end;
4161 // Зажата только левая кнопка мыши:
4162 if (not MouseRDown) and (MouseLDown) and (not MouseMDown) then
4163 begin
4164 // Рисуем прямоугольник планирования панели:
4165 if MouseAction in [MOUSEACTION_DRAWPANEL,
4166 MOUSEACTION_DRAWTRIGGER,
4167 MOUSEACTION_DRAWPRESS] then
4168 begin
4169 if DrawRect = nil then
4170 New(DrawRect);
4171 if ssCtrl in Shift then
4172 begin
4173 wWidth := DotStep;
4174 wHeight := DotStep;
4175 if (lbTextureList.ItemIndex <> -1) and (not IsSpecialTextureSel()) and
4176 (MouseAction = MOUSEACTION_DRAWPANEL) then
4177 begin
4178 if not g_GetTexture(SelectedTexture(), TextureID) then
4179 g_GetTexture('NOTEXTURE', TextureID);
4180 g_GetTextureSizeByID(TextureID, wWidth, wHeight);
4181 end;
4182 DrawRect.Top := MouseLDownPos.y;
4183 DrawRect.Left := MouseLDownPos.x;
4184 DrawRect.Bottom := DrawRect.Top + wHeight;
4185 DrawRect.Right := DrawRect.Left + wWidth;
4186 end
4187 else
4188 begin
4189 DrawRect.Top := MouseLDownPos.y;
4190 DrawRect.Left := MouseLDownPos.x;
4191 DrawRect.Bottom := MousePos.y;
4192 DrawRect.Right := MousePos.x;
4193 end;
4194 end
4195 else // Двигаем карту:
4196 if MouseAction = MOUSEACTION_MOVEMAP then
4197 begin
4198 MoveMap(X, Y);
4199 end;
4200 end;
4202 // Only Middle Mouse Button is pressed
4203 if (not MouseLDown) and (not MouseRDown) and (MouseMDown) then
4204 begin
4205 MapOffset.X := -EnsureRange(-MapOffset.X + MouseMDownPos.X - Mouse.CursorPos.X,
4206 sbHorizontal.Min, sbHorizontal.Max);
4207 sbHorizontal.Position := -MapOffset.X;
4208 MapOffset.Y := -EnsureRange(-MapOffset.Y + MouseMDownPos.Y - Mouse.CursorPos.Y,
4209 sbVertical.Min, sbVertical.Max);
4210 sbVertical.Position := -MapOffset.Y;
4211 MouseMDownPos := Mouse.CursorPos;
4212 end;
4214 // Клавиши мыши не зажаты:
4215 if (not MouseRDown) and (not MouseLDown) then
4216 DrawRect := nil;
4218 // Строка состояния - координаты мыши:
4219 StatusBar.Panels[1].Text := Format('(%d:%d)',
4220 [MousePos.X-MapOffset.X, MousePos.Y-MapOffset.Y]);
4221 end;
4223 procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
4224 begin
4225 CanClose := MessageBox(0, PChar(_lc[I_MSG_EXIT_PROMT]),
4226 PChar(_lc[I_MSG_EXIT]),
4227 MB_ICONQUESTION or MB_YESNO or
4228 MB_DEFBUTTON1) = idYes;
4229 end;
4231 procedure TMainForm.aExitExecute(Sender: TObject);
4232 begin
4233 Close();
4234 end;
4236 procedure TMainForm.FormDestroy(Sender: TObject);
4237 var
4238 config: TConfig;
4239 i: Integer;
4240 begin
4241 config := TConfig.CreateFile(EditorDir+'Editor.cfg');
4243 if WindowState <> wsMaximized then
4244 begin
4245 config.WriteInt('Editor', 'XPos', Left);
4246 config.WriteInt('Editor', 'YPos', Top);
4247 config.WriteInt('Editor', 'Width', Width);
4248 config.WriteInt('Editor', 'Height', Height);
4249 end
4250 else
4251 begin
4252 config.WriteInt('Editor', 'XPos', RestoredLeft);
4253 config.WriteInt('Editor', 'YPos', RestoredTop);
4254 config.WriteInt('Editor', 'Width', RestoredWidth);
4255 config.WriteInt('Editor', 'Height', RestoredHeight);
4256 end;
4257 config.WriteBool('Editor', 'Maximize', WindowState = wsMaximized);
4258 config.WriteBool('Editor', 'Minimap', ShowMap);
4259 config.WriteInt('Editor', 'PanelProps', PanelProps.ClientWidth);
4260 config.WriteInt('Editor', 'PanelObjs', PanelObjs.ClientHeight);
4261 config.WriteBool('Editor', 'DotEnable', DotEnable);
4262 config.WriteInt('Editor', 'DotStep', DotStep);
4263 config.WriteStr('Editor', 'LastOpenDir', OpenDialog.InitialDir);
4264 config.WriteStr('Editor', 'LastSaveDir', SaveDialog.InitialDir);
4265 config.WriteBool('Editor', 'EdgeShow', drEdge[3] < 255);
4266 config.WriteInt('Editor', 'EdgeColor', gColorEdge);
4267 config.WriteInt('Editor', 'EdgeAlpha', gAlphaEdge);
4268 config.WriteInt('Editor', 'LineAlpha', gAlphaTriggerLine);
4269 config.WriteInt('Editor', 'TriggerAlpha', gAlphaTriggerArea);
4270 config.WriteInt('Editor', 'MonsterRectAlpha', gAlphaMonsterRect);
4271 config.WriteInt('Editor', 'AreaRectAlpha', gAlphaAreaRect);
4273 for i := 0 to RecentCount-1 do
4274 if i < RecentFiles.Count then
4275 config.WriteStr('RecentFiles', IntToStr(i+1), RecentFiles[i])
4276 else
4277 config.WriteStr('RecentFiles', IntToStr(i+1), '');
4278 RecentFiles.Free();
4280 config.SaveFile(EditorDir+'Editor.cfg');
4281 config.Free();
4283 slInvalidTextures.Free;
4284 end;
4286 procedure TMainForm.FormDropFiles(Sender: TObject;
4287 const FileNames: array of String);
4288 begin
4289 if Length(FileNames) <> 1 then
4290 Exit;
4292 OpenMapFile(FileNames[0]);
4293 end;
4295 procedure TMainForm.RenderPanelResize(Sender: TObject);
4296 begin
4297 if MainForm.Visible then
4298 MainForm.Resize();
4299 end;
4301 procedure TMainForm.Splitter1Moved(Sender: TObject);
4302 begin
4303 FormResize(Sender);
4304 end;
4306 procedure TMainForm.aMapOptionsExecute(Sender: TObject);
4307 var
4308 ResName: String;
4309 begin
4310 MapOptionsForm.ShowModal();
4312 ResName := OpenedMap;
4313 while (Pos(':\', ResName) > 0) do
4314 Delete(ResName, 1, Pos(':\', ResName) + 1);
4316 UpdateCaption(gMapInfo.Name, ExtractFileName(OpenedWAD), ResName);
4317 end;
4319 procedure TMainForm.aAboutExecute(Sender: TObject);
4320 begin
4321 AboutForm.ShowModal();
4322 end;
4324 procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
4325 var
4326 dx, dy, i: Integer;
4327 FileName: String;
4328 ok: Boolean;
4329 begin
4330 if (not EditingProperties) then
4331 begin
4332 if ssCtrl in Shift then
4333 begin
4334 case Chr(Key) of
4335 '1': ContourEnabled[LAYER_BACK] := not ContourEnabled[LAYER_BACK];
4336 '2': ContourEnabled[LAYER_WALLS] := not ContourEnabled[LAYER_WALLS];
4337 '3': ContourEnabled[LAYER_FOREGROUND] := not ContourEnabled[LAYER_FOREGROUND];
4338 '4': ContourEnabled[LAYER_STEPS] := not ContourEnabled[LAYER_STEPS];
4339 '5': ContourEnabled[LAYER_WATER] := not ContourEnabled[LAYER_WATER];
4340 '6': ContourEnabled[LAYER_ITEMS] := not ContourEnabled[LAYER_ITEMS];
4341 '7': ContourEnabled[LAYER_MONSTERS] := not ContourEnabled[LAYER_MONSTERS];
4342 '8': ContourEnabled[LAYER_AREAS] := not ContourEnabled[LAYER_AREAS];
4343 '9': ContourEnabled[LAYER_TRIGGERS] := not ContourEnabled[LAYER_TRIGGERS];
4344 '0':
4345 begin
4346 ok := False;
4347 for i := Low(ContourEnabled) to High(ContourEnabled) do
4348 if ContourEnabled[i] then
4349 ok := True;
4350 for i := Low(ContourEnabled) to High(ContourEnabled) do
4351 ContourEnabled[i] := not ok
4352 end
4353 end
4354 end
4355 else
4356 begin
4357 case Chr(key) of
4358 '1': SwitchLayer(LAYER_BACK);
4359 '2': SwitchLayer(LAYER_WALLS);
4360 '3': SwitchLayer(LAYER_FOREGROUND);
4361 '4': SwitchLayer(LAYER_STEPS);
4362 '5': SwitchLayer(LAYER_WATER);
4363 '6': SwitchLayer(LAYER_ITEMS);
4364 '7': SwitchLayer(LAYER_MONSTERS);
4365 '8': SwitchLayer(LAYER_AREAS);
4366 '9': SwitchLayer(LAYER_TRIGGERS);
4367 '0': tbShowClick(tbShow);
4368 end
4369 end;
4371 if Key = Ord('V') then
4372 begin // Поворот монстров и областей:
4373 if (SelectedObjects <> nil) then
4374 begin
4375 for i := 0 to High(SelectedObjects) do
4376 if (SelectedObjects[i].Live) then
4377 begin
4378 if (SelectedObjects[i].ObjectType = OBJECT_MONSTER) then
4379 begin
4380 g_ChangeDir(gMonsters[SelectedObjects[i].ID].Direction);
4381 end
4382 else
4383 if (SelectedObjects[i].ObjectType = OBJECT_AREA) then
4384 begin
4385 g_ChangeDir(gAreas[SelectedObjects[i].ID].Direction);
4386 end;
4387 end;
4388 end
4389 else
4390 begin
4391 if pcObjects.ActivePage = tsMonsters then
4392 begin
4393 if rbMonsterLeft.Checked then
4394 rbMonsterRight.Checked := True
4395 else
4396 rbMonsterLeft.Checked := True;
4397 end;
4398 if pcObjects.ActivePage = tsAreas then
4399 begin
4400 if rbAreaLeft.Checked then
4401 rbAreaRight.Checked := True
4402 else
4403 rbAreaLeft.Checked := True;
4404 end;
4405 end;
4406 end;
4408 if not (ssCtrl in Shift) then
4409 begin
4410 // Быстрое превью карты:
4411 if Key = Ord('E') then
4412 begin
4413 if PreviewMode = 0 then
4414 PreviewMode := 2;
4415 end;
4417 // Вертикальный скролл карты:
4418 with sbVertical do
4419 begin
4420 if Key = Ord('W') then
4421 begin
4422 dy := Position;
4423 if ssShift in Shift then Position := EnsureRange(Position - DotStep * 4, Min, Max)
4424 else Position := EnsureRange(Position - DotStep, Min, Max);
4425 MapOffset.Y := -Position;
4426 dy -= Position;
4428 if (MouseLDown or MouseRDown) then
4429 begin
4430 if DrawRect <> nil then
4431 begin
4432 Inc(MouseLDownPos.y, dy);
4433 Inc(MouseRDownPos.y, dy);
4434 end;
4435 Inc(LastMovePoint.Y, dy);
4436 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4437 end;
4438 end;
4440 if Key = Ord('S') then
4441 begin
4442 dy := Position;
4443 if ssShift in Shift then Position := EnsureRange(Position + DotStep * 4, Min, Max)
4444 else Position := EnsureRange(Position + DotStep, Min, Max);
4445 MapOffset.Y := -Position;
4446 dy -= Position;
4448 if (MouseLDown or MouseRDown) then
4449 begin
4450 if DrawRect <> nil then
4451 begin
4452 Inc(MouseLDownPos.y, dy);
4453 Inc(MouseRDownPos.y, dy);
4454 end;
4455 Inc(LastMovePoint.Y, dy);
4456 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4457 end;
4458 end;
4459 end;
4461 // Горизонтальный скролл карты:
4462 with sbHorizontal do
4463 begin
4464 if Key = Ord('A') then
4465 begin
4466 dx := Position;
4467 if ssShift in Shift then Position := EnsureRange(Position - DotStep * 4, Min, Max)
4468 else Position := EnsureRange(Position - DotStep, Min, Max);
4469 MapOffset.X := -Position;
4470 dx -= Position;
4472 if (MouseLDown or MouseRDown) then
4473 begin
4474 if DrawRect <> nil then
4475 begin
4476 Inc(MouseLDownPos.x, dx);
4477 Inc(MouseRDownPos.x, dx);
4478 end;
4479 Inc(LastMovePoint.X, dx);
4480 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4481 end;
4482 end;
4484 if Key = Ord('D') then
4485 begin
4486 dx := Position;
4487 if ssShift in Shift then Position := EnsureRange(Position + DotStep * 4, Min, Max)
4488 else Position := EnsureRange(Position + DotStep, Min, Max);
4489 MapOffset.X := -Position;
4490 dx -= Position;
4492 if (MouseLDown or MouseRDown) then
4493 begin
4494 if DrawRect <> nil then
4495 begin
4496 Inc(MouseLDownPos.x, dx);
4497 Inc(MouseRDownPos.x, dx);
4498 end;
4499 Inc(LastMovePoint.X, dx);
4500 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4501 end;
4502 end;
4503 end;
4504 end
4505 else // ssCtrl in Shift
4506 begin
4507 if ssShift in Shift then
4508 begin
4509 // Вставка по абсолютному смещению:
4510 if Key = Ord('V') then
4511 aPasteObjectExecute(Sender);
4512 end;
4513 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4514 end;
4515 end;
4517 // Удалить выделенные объекты:
4518 if (Key = VK_DELETE) and (SelectedObjects <> nil) and
4519 RenderPanel.Focused() then
4520 DeleteSelectedObjects();
4522 // Снять выделение:
4523 if (Key = VK_ESCAPE) and (SelectedObjects <> nil) then
4524 RemoveSelectFromObjects();
4526 // Передвинуть объекты:
4527 if MainForm.ActiveControl = RenderPanel then
4528 begin
4529 dx := 0;
4530 dy := 0;
4532 if Key = VK_NUMPAD4 then
4533 dx := IfThen(ssAlt in Shift, -1, -DotStep);
4534 if Key = VK_NUMPAD6 then
4535 dx := IfThen(ssAlt in Shift, 1, DotStep);
4536 if Key = VK_NUMPAD8 then
4537 dy := IfThen(ssAlt in Shift, -1, -DotStep);
4538 if Key = VK_NUMPAD5 then
4539 dy := IfThen(ssAlt in Shift, 1, DotStep);
4541 if (dx <> 0) or (dy <> 0) then
4542 begin
4543 MoveSelectedObjects(ssShift in Shift, ssCtrl in Shift, dx, dy);
4544 Key := 0;
4545 end;
4546 end;
4548 if ssCtrl in Shift then
4549 begin
4550 // Выбор панели с текстурой для триггера
4551 if Key = Ord('T') then
4552 begin
4553 DrawPressRect := False;
4554 if SelectFlag = SELECTFLAG_TEXTURE then
4555 begin
4556 SelectFlag := SELECTFLAG_NONE;
4557 Exit;
4558 end;
4559 vleObjectProperty.FindRow(_lc[I_PROP_TR_TEXTURE_PANEL], i);
4560 if i > 0 then
4561 SelectFlag := SELECTFLAG_TEXTURE;
4562 end;
4564 if Key = Ord('D') then
4565 begin
4566 SelectFlag := SELECTFLAG_NONE;
4567 if DrawPressRect then
4568 begin
4569 DrawPressRect := False;
4570 Exit;
4571 end;
4572 i := -1;
4574 // Выбор области воздействия, в зависимости от типа триггера
4575 vleObjectProperty.FindRow(_lc[I_PROP_TR_EX_AREA], i);
4576 if i > 0 then
4577 begin
4578 DrawPressRect := True;
4579 Exit;
4580 end;
4581 vleObjectProperty.FindRow(_lc[I_PROP_TR_DOOR_PANEL], i);
4582 if i <= 0 then
4583 vleObjectProperty.FindRow(_lc[I_PROP_TR_TRAP_PANEL], i);
4584 if i > 0 then
4585 begin
4586 SelectFlag := SELECTFLAG_DOOR;
4587 Exit;
4588 end;
4589 vleObjectProperty.FindRow(_lc[I_PROP_TR_LIFT_PANEL], i);
4590 if i > 0 then
4591 begin
4592 SelectFlag := SELECTFLAG_LIFT;
4593 Exit;
4594 end;
4595 vleObjectProperty.FindRow(_lc[I_PROP_TR_TELEPORT_TO], i);
4596 if i > 0 then
4597 begin
4598 SelectFlag := SELECTFLAG_TELEPORT;
4599 Exit;
4600 end;
4601 vleObjectProperty.FindRow(_lc[I_PROP_TR_SPAWN_TO], i);
4602 if i > 0 then
4603 begin
4604 SelectFlag := SELECTFLAG_SPAWNPOINT;
4605 Exit;
4606 end;
4608 // Выбор основного параметра, в зависимости от типа триггера
4609 vleObjectProperty.FindRow(_lc[I_PROP_TR_NEXT_MAP], i);
4610 if i > 0 then
4611 begin
4612 g_ProcessResourceStr(OpenedMap, @FileName, nil, nil);
4613 SelectMapForm.Caption := _lc[I_CAP_SELECT];
4614 SelectMapForm.GetMaps(FileName);
4616 if SelectMapForm.ShowModal() = mrOK then
4617 begin
4618 vleObjectProperty.Cells[1, i] := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
4619 bApplyProperty.Click();
4620 end;
4621 Exit;
4622 end;
4623 vleObjectProperty.FindRow(_lc[I_PROP_TR_SOUND_NAME], i);
4624 if i <= 0 then
4625 vleObjectProperty.FindRow(_lc[I_PROP_TR_MUSIC_NAME], i);
4626 if i > 0 then
4627 begin
4628 AddSoundForm.OKFunction := nil;
4629 AddSoundForm.lbResourcesList.MultiSelect := False;
4630 AddSoundForm.SetResource := vleObjectProperty.Cells[1, i];
4632 if (AddSoundForm.ShowModal() = mrOk) then
4633 begin
4634 vleObjectProperty.Cells[1, i] := AddSoundForm.ResourceName;
4635 bApplyProperty.Click();
4636 end;
4637 Exit;
4638 end;
4639 vleObjectProperty.FindRow(_lc[I_PROP_TR_PUSH_ANGLE], i);
4640 if i <= 0 then
4641 vleObjectProperty.FindRow(_lc[I_PROP_TR_MESSAGE_TEXT], i);
4642 if i > 0 then
4643 begin
4644 vleObjectProperty.Row := i;
4645 vleObjectProperty.SetFocus();
4646 Exit;
4647 end;
4648 end;
4649 end;
4650 end;
4652 procedure TMainForm.aOptimizeExecute(Sender: TObject);
4653 begin
4654 RemoveSelectFromObjects();
4655 MapOptimizationForm.ShowModal();
4656 end;
4658 procedure TMainForm.aCheckMapExecute(Sender: TObject);
4659 begin
4660 MapCheckForm.ShowModal();
4661 end;
4663 procedure TMainForm.bbAddTextureClick(Sender: TObject);
4664 begin
4665 AddTextureForm.lbResourcesList.MultiSelect := True;
4666 AddTextureForm.ShowModal();
4667 end;
4669 procedure TMainForm.lbTextureListClick(Sender: TObject);
4670 var
4671 TextureID: DWORD;
4672 TextureWidth, TextureHeight: Word;
4673 begin
4674 TextureID := 0;
4675 TextureWidth := 0;
4676 TextureHeight := 0;
4677 if (lbTextureList.ItemIndex <> -1) and
4678 (not IsSpecialTextureSel()) then
4679 begin
4680 if g_GetTexture(SelectedTexture(), TextureID) then
4681 begin
4682 g_GetTextureSizeByID(TextureID, TextureWidth, TextureHeight);
4684 lTextureWidth.Caption := IntToStr(TextureWidth);
4685 lTextureHeight.Caption := IntToStr(TextureHeight);
4686 end else
4687 begin
4688 lTextureWidth.Caption := _lc[I_NOT_ACCESSIBLE];
4689 lTextureHeight.Caption := _lc[I_NOT_ACCESSIBLE];
4690 end;
4691 end
4692 else
4693 begin
4694 lTextureWidth.Caption := '';
4695 lTextureHeight.Caption := '';
4696 end;
4697 end;
4699 procedure TMainForm.lbTextureListDrawItem(Control: TWinControl; Index: Integer;
4700 ARect: TRect; State: TOwnerDrawState);
4701 begin
4702 with Control as TListBox do
4703 begin
4704 if LCLType.odSelected in State then
4705 begin
4706 Canvas.Brush.Color := clHighlight;
4707 Canvas.Font.Color := clHighlightText;
4708 end else
4709 if (Items <> nil) and (Index >= 0) then
4710 if slInvalidTextures.IndexOf(Items[Index]) > -1 then
4711 begin
4712 Canvas.Brush.Color := clRed;
4713 Canvas.Font.Color := clWhite;
4714 end;
4715 Canvas.FillRect(ARect);
4716 Canvas.TextRect(ARect, ARect.Left, ARect.Top, Items[Index]);
4717 end;
4718 end;
4720 procedure TMainForm.miReopenMapClick(Sender: TObject);
4721 var
4722 FileName, Resource: String;
4723 begin
4724 if OpenedMap = '' then
4725 Exit;
4727 if MessageBox(0, PChar(_lc[I_MSG_REOPEN_MAP_PROMT]),
4728 PChar(_lc[I_MENU_FILE_REOPEN]), MB_ICONQUESTION or MB_YESNO) <> idYes then
4729 Exit;
4731 g_ProcessResourceStr(OpenedMap, @FileName, nil, @Resource);
4732 OpenMap(FileName, Resource);
4733 end;
4735 procedure TMainForm.vleObjectPropertyGetPickList(Sender: TObject;
4736 const KeyName: String; Values: TStrings);
4737 begin
4738 if vleObjectProperty.ItemProps[KeyName].EditStyle = esPickList then
4739 begin
4740 if KeyName = _lc[I_PROP_DIRECTION] then
4741 begin
4742 Values.Add(DirNames[D_LEFT]);
4743 Values.Add(DirNames[D_RIGHT]);
4744 end
4745 else if KeyName = _lc[I_PROP_TR_TELEPORT_DIR] then
4746 begin
4747 Values.Add(DirNamesAdv[0]);
4748 Values.Add(DirNamesAdv[1]);
4749 Values.Add(DirNamesAdv[2]);
4750 Values.Add(DirNamesAdv[3]);
4751 end
4752 else if KeyName = _lc[I_PROP_TR_MUSIC_ACT] then
4753 begin
4754 Values.Add(_lc[I_PROP_TR_MUSIC_ON]);
4755 Values.Add(_lc[I_PROP_TR_MUSIC_OFF]);
4756 end
4757 else if KeyName = _lc[I_PROP_TR_MONSTER_BEHAVIOUR] then
4758 begin
4759 Values.Add(_lc[I_PROP_TR_MONSTER_BEHAVIOUR_0]);
4760 Values.Add(_lc[I_PROP_TR_MONSTER_BEHAVIOUR_1]);
4761 Values.Add(_lc[I_PROP_TR_MONSTER_BEHAVIOUR_2]);
4762 Values.Add(_lc[I_PROP_TR_MONSTER_BEHAVIOUR_3]);
4763 Values.Add(_lc[I_PROP_TR_MONSTER_BEHAVIOUR_4]);
4764 Values.Add(_lc[I_PROP_TR_MONSTER_BEHAVIOUR_5]);
4765 end
4766 else if KeyName = _lc[I_PROP_TR_SCORE_ACT] then
4767 begin
4768 Values.Add(_lc[I_PROP_TR_SCORE_ACT_0]);
4769 Values.Add(_lc[I_PROP_TR_SCORE_ACT_1]);
4770 Values.Add(_lc[I_PROP_TR_SCORE_ACT_2]);
4771 Values.Add(_lc[I_PROP_TR_SCORE_ACT_3]);
4772 end
4773 else if KeyName = _lc[I_PROP_TR_SCORE_TEAM] then
4774 begin
4775 Values.Add(_lc[I_PROP_TR_SCORE_TEAM_0]);
4776 Values.Add(_lc[I_PROP_TR_SCORE_TEAM_1]);
4777 Values.Add(_lc[I_PROP_TR_SCORE_TEAM_2]);
4778 Values.Add(_lc[I_PROP_TR_SCORE_TEAM_3]);
4779 end
4780 else if KeyName = _lc[I_PROP_TR_MESSAGE_KIND] then
4781 begin
4782 Values.Add(_lc[I_PROP_TR_MESSAGE_KIND_0]);
4783 Values.Add(_lc[I_PROP_TR_MESSAGE_KIND_1]);
4784 end
4785 else if KeyName = _lc[I_PROP_TR_MESSAGE_TO] then
4786 begin
4787 Values.Add(_lc[I_PROP_TR_MESSAGE_TO_0]);
4788 Values.Add(_lc[I_PROP_TR_MESSAGE_TO_1]);
4789 Values.Add(_lc[I_PROP_TR_MESSAGE_TO_2]);
4790 Values.Add(_lc[I_PROP_TR_MESSAGE_TO_3]);
4791 Values.Add(_lc[I_PROP_TR_MESSAGE_TO_4]);
4792 Values.Add(_lc[I_PROP_TR_MESSAGE_TO_5]);
4793 end
4794 else if KeyName = _lc[I_PROP_TR_SHOT_TO] then
4795 begin
4796 Values.Add(_lc[I_PROP_TR_SHOT_TO_0]);
4797 Values.Add(_lc[I_PROP_TR_SHOT_TO_1]);
4798 Values.Add(_lc[I_PROP_TR_SHOT_TO_2]);
4799 Values.Add(_lc[I_PROP_TR_SHOT_TO_3]);
4800 Values.Add(_lc[I_PROP_TR_SHOT_TO_4]);
4801 Values.Add(_lc[I_PROP_TR_SHOT_TO_5]);
4802 Values.Add(_lc[I_PROP_TR_SHOT_TO_6]);
4803 end
4804 else if KeyName = _lc[I_PROP_TR_SHOT_AIM] then
4805 begin
4806 Values.Add(_lc[I_PROP_TR_SHOT_AIM_0]);
4807 Values.Add(_lc[I_PROP_TR_SHOT_AIM_1]);
4808 Values.Add(_lc[I_PROP_TR_SHOT_AIM_2]);
4809 Values.Add(_lc[I_PROP_TR_SHOT_AIM_3]);
4810 end
4811 else if KeyName = _lc[I_PROP_TR_DAMAGE_KIND] then
4812 begin
4813 Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_0]);
4814 Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_3]);
4815 Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_4]);
4816 Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_5]);
4817 Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_6]);
4818 Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_7]);
4819 Values.Add(_lc[I_PROP_TR_DAMAGE_KIND_8]);
4820 end
4821 else if (KeyName = _lc[I_PROP_PANEL_BLEND]) or
4822 (KeyName = _lc[I_PROP_DM_ONLY]) or
4823 (KeyName = _lc[I_PROP_ITEM_FALLS]) or
4824 (KeyName = _lc[I_PROP_TR_ENABLED]) or
4825 (KeyName = _lc[I_PROP_TR_D2D]) or
4826 (KeyName = _lc[I_PROP_TR_SILENT]) or
4827 (KeyName = _lc[I_PROP_TR_TELEPORT_SILENT]) or
4828 (KeyName = _lc[I_PROP_TR_EX_RANDOM]) or
4829 (KeyName = _lc[I_PROP_TR_TEXTURE_ONCE]) or
4830 (KeyName = _lc[I_PROP_TR_TEXTURE_ANIM_ONCE]) or
4831 (KeyName = _lc[I_PROP_TR_SOUND_LOCAL]) or
4832 (KeyName = _lc[I_PROP_TR_SOUND_SWITCH]) or
4833 (KeyName = _lc[I_PROP_TR_MONSTER_ACTIVE]) or
4834 (KeyName = _lc[I_PROP_TR_PUSH_RESET]) or
4835 (KeyName = _lc[I_PROP_TR_SCORE_CON]) or
4836 (KeyName = _lc[I_PROP_TR_SCORE_MSG]) or
4837 (KeyName = _lc[I_PROP_TR_HEALTH_MAX]) or
4838 (KeyName = _lc[I_PROP_TR_SHOT_SOUND]) or
4839 (KeyName = _lc[I_PROP_TR_EFFECT_CENTER]) then
4840 begin
4841 Values.Add(BoolNames[True]);
4842 Values.Add(BoolNames[False]);
4843 end;
4844 end;
4845 end;
4847 procedure TMainForm.bApplyPropertyClick(Sender: TObject);
4848 var
4849 _id, a, r, c: Integer;
4850 s: String;
4851 res: Boolean;
4852 NoTextureID: DWORD;
4853 NW, NH: Word;
4854 begin
4855 NoTextureID := 0;
4856 NW := 0;
4857 NH := 0;
4859 if SelectedObjectCount() <> 1 then
4860 Exit;
4861 if not SelectedObjects[GetFirstSelected()].Live then
4862 Exit;
4864 try
4865 if not CheckProperty() then
4866 Exit;
4867 except
4868 Exit;
4869 end;
4871 _id := GetFirstSelected();
4873 r := vleObjectProperty.Row;
4874 c := vleObjectProperty.Col;
4876 case SelectedObjects[_id].ObjectType of
4877 OBJECT_PANEL:
4878 begin
4879 with gPanels[SelectedObjects[_id].ID] do
4880 begin
4881 X := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_X]]));
4882 Y := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_Y]]));
4883 Width := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_WIDTH]]));
4884 Height := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_HEIGHT]]));
4886 PanelType := GetPanelType(vleObjectProperty.Values[_lc[I_PROP_PANEL_TYPE]]);
4888 // Сброс ссылки на триггеры смены текстуры:
4889 if not WordBool(PanelType and (PANEL_WALL or PANEL_FORE or PANEL_BACK)) then
4890 if gTriggers <> nil then
4891 for a := 0 to High(gTriggers) do
4892 begin
4893 if (gTriggers[a].TriggerType <> 0) and
4894 (gTriggers[a].TexturePanel = Integer(SelectedObjects[_id].ID)) then
4895 gTriggers[a].TexturePanel := -1;
4896 if (gTriggers[a].TriggerType = TRIGGER_SHOT) and
4897 (gTriggers[a].Data.ShotPanelID = Integer(SelectedObjects[_id].ID)) then
4898 gTriggers[a].Data.ShotPanelID := -1;
4899 end;
4901 // Сброс ссылки на триггеры лифта:
4902 if not WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
4903 if gTriggers <> nil then
4904 for a := 0 to High(gTriggers) do
4905 if (gTriggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT]) and
4906 (gTriggers[a].Data.PanelID = Integer(SelectedObjects[_id].ID)) then
4907 gTriggers[a].Data.PanelID := -1;
4909 // Сброс ссылки на триггеры двери:
4910 if not WordBool(PanelType and (PANEL_OPENDOOR or PANEL_CLOSEDOOR)) then
4911 if gTriggers <> nil then
4912 for a := 0 to High(gTriggers) do
4913 if (gTriggers[a].TriggerType in [TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
4914 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP]) and
4915 (gTriggers[a].Data.PanelID = Integer(SelectedObjects[_id].ID)) then
4916 gTriggers[a].Data.PanelID := -1;
4918 if IsTexturedPanel(PanelType) then
4919 begin // Может быть текстура
4920 if TextureName <> '' then
4921 begin // Была текстура
4922 Alpha := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_PANEL_ALPHA]]));
4923 Blending := NameToBool(vleObjectProperty.Values[_lc[I_PROP_PANEL_BLEND]]);
4924 end
4925 else // Не было
4926 begin
4927 Alpha := 0;
4928 Blending := False;
4929 end;
4931 // Новая текстура:
4932 TextureName := vleObjectProperty.Values[_lc[I_PROP_PANEL_TEX]];
4934 if TextureName <> '' then
4935 begin // Есть текстура
4936 // Обычная текстура:
4937 if not IsSpecialTexture(TextureName) then
4938 begin
4939 g_GetTextureSizeByName(TextureName,
4940 TextureWidth, TextureHeight);
4942 // Проверка кратности размеров панели:
4943 res := True;
4944 if TextureWidth <> 0 then
4945 if gPanels[SelectedObjects[_id].ID].Width mod TextureWidth <> 0 then
4946 begin
4947 ErrorMessageBox(Format(_lc[I_MSG_WRONG_TEXWIDTH],
4948 [TextureWidth]));
4949 Res := False;
4950 end;
4951 if Res and (TextureHeight <> 0) then
4952 if gPanels[SelectedObjects[_id].ID].Height mod TextureHeight <> 0 then
4953 begin
4954 ErrorMessageBox(Format(_lc[I_MSG_WRONG_TEXHEIGHT],
4955 [TextureHeight]));
4956 Res := False;
4957 end;
4959 if Res then
4960 begin
4961 if not g_GetTexture(TextureName, TextureID) then
4962 // Не удалось загрузить текстуру, рисуем NOTEXTURE
4963 if g_GetTexture('NOTEXTURE', NoTextureID) then
4964 begin
4965 TextureID := TEXTURE_SPECIAL_NOTEXTURE;
4966 g_GetTextureSizeByID(NoTextureID, NW, NH);
4967 TextureWidth := NW;
4968 TextureHeight := NH;
4969 end else
4970 begin
4971 TextureID := TEXTURE_SPECIAL_NONE;
4972 TextureWidth := 1;
4973 TextureHeight := 1;
4974 end;
4975 end
4976 else
4977 begin
4978 TextureName := '';
4979 TextureWidth := 1;
4980 TextureHeight := 1;
4981 TextureID := TEXTURE_SPECIAL_NONE;
4982 end;
4983 end
4984 else // Спец.текстура
4985 begin
4986 TextureHeight := 1;
4987 TextureWidth := 1;
4988 TextureID := SpecialTextureID(TextureName);
4989 end;
4990 end
4991 else // Нет текстуры
4992 begin
4993 TextureWidth := 1;
4994 TextureHeight := 1;
4995 TextureID := TEXTURE_SPECIAL_NONE;
4996 end;
4997 end
4998 else // Не может быть текстуры
4999 begin
5000 Alpha := 0;
5001 Blending := False;
5002 TextureName := '';
5003 TextureWidth := 1;
5004 TextureHeight := 1;
5005 TextureID := TEXTURE_SPECIAL_NONE;
5006 end;
5007 end;
5008 end;
5010 OBJECT_ITEM:
5011 begin
5012 with gItems[SelectedObjects[_id].ID] do
5013 begin
5014 X := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_X]]));
5015 Y := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_Y]]));
5016 OnlyDM := NameToBool(vleObjectProperty.Values[_lc[I_PROP_DM_ONLY]]);
5017 Fall := NameToBool(vleObjectProperty.Values[_lc[I_PROP_ITEM_FALLS]]);
5018 end;
5019 end;
5021 OBJECT_MONSTER:
5022 begin
5023 with gMonsters[SelectedObjects[_id].ID] do
5024 begin
5025 X := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_X]]));
5026 Y := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_Y]]));
5027 Direction := NameToDir(vleObjectProperty.Values[_lc[I_PROP_DIRECTION]]);
5028 end;
5029 end;
5031 OBJECT_AREA:
5032 begin
5033 with gAreas[SelectedObjects[_id].ID] do
5034 begin
5035 X := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_X]]));
5036 Y := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_Y]]));
5037 Direction := NameToDir(vleObjectProperty.Values[_lc[I_PROP_DIRECTION]]);
5038 end;
5039 end;
5041 OBJECT_TRIGGER:
5042 begin
5043 with gTriggers[SelectedObjects[_id].ID] do
5044 begin
5045 X := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_X]]));
5046 Y := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_Y]]));
5047 Width := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_WIDTH]]));
5048 Height := StrToInt(Trim(vleObjectProperty.Values[_lc[I_PROP_HEIGHT]]));
5049 Enabled := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_ENABLED]]);
5050 ActivateType := StrToActivate(vleObjectProperty.Values[_lc[I_PROP_TR_ACTIVATION]]);
5051 Key := StrToKey(vleObjectProperty.Values[_lc[I_PROP_TR_KEYS]]);
5053 case TriggerType of
5054 TRIGGER_EXIT:
5055 begin
5056 s := utf2win(vleObjectProperty.Values[_lc[I_PROP_TR_NEXT_MAP]]);
5057 FillByte(Data.MapName[0], 16, 0);
5058 if s <> '' then
5059 Move(s[1], Data.MapName[0], Min(Length(s), 16));
5060 end;
5062 TRIGGER_TEXTURE:
5063 begin
5064 Data.ActivateOnce := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_TEXTURE_ONCE]]);
5065 Data.AnimOnce := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_TEXTURE_ANIM_ONCE]]);
5066 end;
5068 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
5069 begin
5070 Data.Wait := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EX_DELAY]], 0), 65535);
5071 Data.Count := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EX_COUNT]], 0), 65535);
5072 if Data.Count < 1 then
5073 Data.Count := 1;
5074 if TriggerType = TRIGGER_PRESS then
5075 Data.ExtRandom := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_EX_RANDOM]]);
5076 end;
5078 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
5079 TRIGGER_CLOSETRAP, TRIGGER_TRAP, TRIGGER_LIFTUP, TRIGGER_LIFTDOWN,
5080 TRIGGER_LIFT:
5081 begin
5082 Data.NoSound := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_SILENT]]);
5083 Data.d2d_doors := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_D2D]]);
5084 end;
5086 TRIGGER_TELEPORT:
5087 begin
5088 Data.d2d_teleport := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_D2D]]);
5089 Data.silent_teleport := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_TELEPORT_SILENT]]);
5090 Data.TlpDir := NameToDirAdv(vleObjectProperty.Values[_lc[I_PROP_TR_TELEPORT_DIR]]);
5091 end;
5093 TRIGGER_SOUND:
5094 begin
5095 s := utf2win(vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_NAME]]);
5096 FillByte(Data.SoundName[0], 64, 0);
5097 if s <> '' then
5098 Move(s[1], Data.SoundName[0], Min(Length(s), 64));
5100 Data.Volume := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_VOLUME]], 0), 255);
5101 Data.Pan := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_PAN]], 0), 255);
5102 Data.PlayCount := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_COUNT]], 0), 255);
5103 Data.Local := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_LOCAL]]);
5104 Data.SoundSwitch := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_SOUND_SWITCH]]);
5105 end;
5107 TRIGGER_SPAWNMONSTER:
5108 begin
5109 Data.MonType := StrToMonster(vleObjectProperty.Values[_lc[I_PROP_TR_MONSTER_TYPE]]);
5110 Data.MonDir := Byte(NameToDir(vleObjectProperty.Values[_lc[I_PROP_DIRECTION]]));
5111 Data.MonHealth := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_HEALTH]], 0), 1000000);
5112 if Data.MonHealth < 0 then
5113 Data.MonHealth := 0;
5114 Data.MonActive := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_MONSTER_ACTIVE]]);
5115 Data.MonCount := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_COUNT]], 0), 64);
5116 if Data.MonCount < 1 then
5117 Data.MonCount := 1;
5118 Data.MonEffect := StrToEffect(vleObjectProperty.Values[_lc[I_PROP_TR_FX_TYPE]]);
5119 Data.MonMax := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SPAWN_MAX]], 0), 65535);
5120 Data.MonDelay := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SPAWN_DELAY]], 0), 65535);
5121 Data.MonBehav := 0;
5122 if vleObjectProperty.Values[_lc[I_PROP_TR_MONSTER_BEHAVIOUR]] = _lc[I_PROP_TR_MONSTER_BEHAVIOUR_1] then
5123 Data.MonBehav := 1;
5124 if vleObjectProperty.Values[_lc[I_PROP_TR_MONSTER_BEHAVIOUR]] = _lc[I_PROP_TR_MONSTER_BEHAVIOUR_2] then
5125 Data.MonBehav := 2;
5126 if vleObjectProperty.Values[_lc[I_PROP_TR_MONSTER_BEHAVIOUR]] = _lc[I_PROP_TR_MONSTER_BEHAVIOUR_3] then
5127 Data.MonBehav := 3;
5128 if vleObjectProperty.Values[_lc[I_PROP_TR_MONSTER_BEHAVIOUR]] = _lc[I_PROP_TR_MONSTER_BEHAVIOUR_4] then
5129 Data.MonBehav := 4;
5130 if vleObjectProperty.Values[_lc[I_PROP_TR_MONSTER_BEHAVIOUR]] = _lc[I_PROP_TR_MONSTER_BEHAVIOUR_5] then
5131 Data.MonBehav := 5;
5132 end;
5134 TRIGGER_SPAWNITEM:
5135 begin
5136 Data.ItemType := StrToItem(vleObjectProperty.Values[_lc[I_PROP_TR_ITEM_TYPE]]);
5137 Data.ItemOnlyDM := NameToBool(vleObjectProperty.Values[_lc[I_PROP_DM_ONLY]]);
5138 Data.ItemFalls := NameToBool(vleObjectProperty.Values[_lc[I_PROP_ITEM_FALLS]]);
5139 Data.ItemCount := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_COUNT]], 0), 64);
5140 if Data.ItemCount < 1 then
5141 Data.ItemCount := 1;
5142 Data.ItemEffect := StrToEffect(vleObjectProperty.Values[_lc[I_PROP_TR_FX_TYPE]]);
5143 Data.ItemMax := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SPAWN_MAX]], 0), 65535);
5144 Data.ItemDelay := Min(StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SPAWN_DELAY]], 0), 65535);
5145 end;
5147 TRIGGER_MUSIC:
5148 begin
5149 s := utf2win(vleObjectProperty.Values[_lc[I_PROP_TR_MUSIC_NAME]]);
5150 FillByte(Data.MusicName[0], 64, 0);
5151 if s <> '' then
5152 Move(s[1], Data.MusicName[0], Min(Length(s), 64));
5154 if vleObjectProperty.Values[_lc[I_PROP_TR_MUSIC_ACT]] = _lc[I_PROP_TR_MUSIC_ON] then
5155 Data.MusicAction := 1
5156 else
5157 Data.MusicAction := 0;
5158 end;
5160 TRIGGER_PUSH:
5161 begin
5162 Data.PushAngle := Min(
5163 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_PUSH_ANGLE]], 0), 360);
5164 Data.PushForce := Min(
5165 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_PUSH_FORCE]], 0), 255);
5166 Data.ResetVel := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_PUSH_RESET]]);
5167 end;
5169 TRIGGER_SCORE:
5170 begin
5171 Data.ScoreAction := 0;
5172 if vleObjectProperty.Values[_lc[I_PROP_TR_SCORE_ACT]] = _lc[I_PROP_TR_SCORE_ACT_1] then
5173 Data.ScoreAction := 1
5174 else if vleObjectProperty.Values[_lc[I_PROP_TR_SCORE_ACT]] = _lc[I_PROP_TR_SCORE_ACT_2] then
5175 Data.ScoreAction := 2
5176 else if vleObjectProperty.Values[_lc[I_PROP_TR_SCORE_ACT]] = _lc[I_PROP_TR_SCORE_ACT_3] then
5177 Data.ScoreAction := 3;
5178 Data.ScoreCount := Min(Max(
5179 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_COUNT]], 0), 0), 255);
5180 Data.ScoreTeam := 0;
5181 if vleObjectProperty.Values[_lc[I_PROP_TR_SCORE_TEAM]] = _lc[I_PROP_TR_SCORE_TEAM_1] then
5182 Data.ScoreTeam := 1
5183 else if vleObjectProperty.Values[_lc[I_PROP_TR_SCORE_TEAM]] = _lc[I_PROP_TR_SCORE_TEAM_2] then
5184 Data.ScoreTeam := 2
5185 else if vleObjectProperty.Values[_lc[I_PROP_TR_SCORE_TEAM]] = _lc[I_PROP_TR_SCORE_TEAM_3] then
5186 Data.ScoreTeam := 3;
5187 Data.ScoreCon := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_SCORE_CON]]);
5188 Data.ScoreMsg := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_SCORE_MSG]]);
5189 end;
5191 TRIGGER_MESSAGE:
5192 begin
5193 Data.MessageKind := 0;
5194 if vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_KIND]] = _lc[I_PROP_TR_MESSAGE_KIND_1] then
5195 Data.MessageKind := 1;
5197 Data.MessageSendTo := 0;
5198 if vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TO]] = _lc[I_PROP_TR_MESSAGE_TO_1] then
5199 Data.MessageSendTo := 1
5200 else if vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TO]] = _lc[I_PROP_TR_MESSAGE_TO_2] then
5201 Data.MessageSendTo := 2
5202 else if vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TO]] = _lc[I_PROP_TR_MESSAGE_TO_3] then
5203 Data.MessageSendTo := 3
5204 else if vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TO]] = _lc[I_PROP_TR_MESSAGE_TO_4] then
5205 Data.MessageSendTo := 4
5206 else if vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TO]] = _lc[I_PROP_TR_MESSAGE_TO_5] then
5207 Data.MessageSendTo := 5;
5209 s := utf2win(vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TEXT]]);
5210 FillByte(Data.MessageText[0], 100, 0);
5211 if s <> '' then
5212 Move(s[1], Data.MessageText[0], Min(Length(s), 100));
5214 Data.MessageTime := Min(Max(
5215 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_MESSAGE_TIME]], 0), 0), 65535);
5216 end;
5218 TRIGGER_DAMAGE:
5219 begin
5220 Data.DamageValue := Min(Max(
5221 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_DAMAGE_VALUE]], 0), 0), 65535);
5222 Data.DamageInterval := Min(Max(
5223 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_INTERVAL]], 0), 0), 65535);
5224 s := vleObjectProperty.Values[_lc[I_PROP_TR_DAMAGE_KIND]];
5225 if s = _lc[I_PROP_TR_DAMAGE_KIND_3] then
5226 Data.DamageKind := 3
5227 else if s = _lc[I_PROP_TR_DAMAGE_KIND_4] then
5228 Data.DamageKind := 4
5229 else if s = _lc[I_PROP_TR_DAMAGE_KIND_5] then
5230 Data.DamageKind := 5
5231 else if s = _lc[I_PROP_TR_DAMAGE_KIND_6] then
5232 Data.DamageKind := 6
5233 else if s = _lc[I_PROP_TR_DAMAGE_KIND_7] then
5234 Data.DamageKind := 7
5235 else if s = _lc[I_PROP_TR_DAMAGE_KIND_8] then
5236 Data.DamageKind := 8
5237 else
5238 Data.DamageKind := 0;
5239 end;
5241 TRIGGER_HEALTH:
5242 begin
5243 Data.HealValue := Min(Max(
5244 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_HEALTH]], 0), 0), 65535);
5245 Data.HealInterval := Min(Max(
5246 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_INTERVAL]], 0), 0), 65535);
5247 Data.HealMax := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_HEALTH_MAX]]);
5248 Data.HealSilent := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_SILENT]]);
5249 end;
5251 TRIGGER_SHOT:
5252 begin
5253 Data.ShotType := StrToShot(vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_TYPE]]);
5254 Data.ShotSound := NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_SOUND]]);
5255 Data.ShotTarget := 0;
5256 if vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_TO]] = _lc[I_PROP_TR_SHOT_TO_1] then
5257 Data.ShotTarget := 1
5258 else if vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_TO]] = _lc[I_PROP_TR_SHOT_TO_2] then
5259 Data.ShotTarget := 2
5260 else if vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_TO]] = _lc[I_PROP_TR_SHOT_TO_3] then
5261 Data.ShotTarget := 3
5262 else if vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_TO]] = _lc[I_PROP_TR_SHOT_TO_4] then
5263 Data.ShotTarget := 4
5264 else if vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_TO]] = _lc[I_PROP_TR_SHOT_TO_5] then
5265 Data.ShotTarget := 5
5266 else if vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_TO]] = _lc[I_PROP_TR_SHOT_TO_6] then
5267 Data.ShotTarget := 6;
5268 Data.ShotIntSight := Min(Max(
5269 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_SIGHT]], 0), 0), 65535);
5270 Data.ShotAim := 0;
5271 if vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_AIM]] = _lc[I_PROP_TR_SHOT_AIM_1] then
5272 Data.ShotAim := 1
5273 else if vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_AIM]] = _lc[I_PROP_TR_SHOT_AIM_2] then
5274 Data.ShotAim := 2
5275 else if vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_AIM]] = _lc[I_PROP_TR_SHOT_AIM_3] then
5276 Data.ShotAim := 3;
5277 Data.ShotAngle := Min(
5278 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_ANGLE]], 0), 360);
5279 Data.ShotWait := Min(Max(
5280 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EX_DELAY]], 0), 0), 65535);
5281 Data.ShotAccuracy := Min(Max(
5282 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_ACC]], 0), 0), 65535);
5283 Data.ShotAmmo := Min(Max(
5284 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_AMMO]], 0), 0), 65535);
5285 Data.ShotIntReload := Min(Max(
5286 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_SHOT_RELOAD]], 0), 0), 65535);
5287 end;
5289 TRIGGER_EFFECT:
5290 begin
5291 Data.FXCount := Min(Max(
5292 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_COUNT]], 0), 0), 255);
5293 if vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_TYPE]] = _lc[I_PROP_TR_EFFECT_PARTICLE] then
5294 begin
5295 Data.FXType := TRIGGER_EFFECT_PARTICLE;
5296 Data.FXSubType := TRIGGER_EFFECT_SLIQUID;
5297 if vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SUBTYPE]] = _lc[I_PROP_TR_EFFECT_SLIQUID] then
5298 Data.FXSubType := TRIGGER_EFFECT_SLIQUID
5299 else if vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SUBTYPE]] = _lc[I_PROP_TR_EFFECT_LLIQUID] then
5300 Data.FXSubType := TRIGGER_EFFECT_LLIQUID
5301 else if vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SUBTYPE]] = _lc[I_PROP_TR_EFFECT_DLIQUID] then
5302 Data.FXSubType := TRIGGER_EFFECT_DLIQUID
5303 else if vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SUBTYPE]] = _lc[I_PROP_TR_EFFECT_BLOOD] then
5304 Data.FXSubType := TRIGGER_EFFECT_BLOOD
5305 else if vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SUBTYPE]] = _lc[I_PROP_TR_EFFECT_SPARK] then
5306 Data.FXSubType := TRIGGER_EFFECT_SPARK
5307 else if vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SUBTYPE]] = _lc[I_PROP_TR_EFFECT_BUBBLE] then
5308 Data.FXSubType := TRIGGER_EFFECT_BUBBLE;
5309 end else
5310 begin
5311 Data.FXType := TRIGGER_EFFECT_ANIMATION;
5312 Data.FXSubType := StrToEffect(vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SUBTYPE]]);
5313 end;
5314 a := Min(Max(
5315 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_COLOR]], 0), 0), $FFFFFF);
5316 Data.FXColorR := a and $FF;
5317 Data.FXColorG := (a shr 8) and $FF;
5318 Data.FXColorB := (a shr 16) and $FF;
5319 if NameToBool(vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_CENTER]]) then
5320 Data.FXPos := 0
5321 else
5322 Data.FXPos := 1;
5323 Data.FXWait := Min(Max(
5324 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EX_DELAY]], 0), 0), 65535);
5325 Data.FXVelX := Min(Max(
5326 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_VELX]], 0), -128), 127);
5327 Data.FXVelY := Min(Max(
5328 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_VELY]], 0), -128), 127);
5329 Data.FXSpreadL := Min(Max(
5330 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SPL]], 0), 0), 255);
5331 Data.FXSpreadR := Min(Max(
5332 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SPR]], 0), 0), 255);
5333 Data.FXSpreadU := Min(Max(
5334 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SPU]], 0), 0), 255);
5335 Data.FXSpreadD := Min(Max(
5336 StrToIntDef(vleObjectProperty.Values[_lc[I_PROP_TR_EFFECT_SPD]], 0), 0), 255);
5337 end;
5338 end;
5339 end;
5340 end;
5341 end;
5343 FillProperty();
5345 vleObjectProperty.Row := r;
5346 vleObjectProperty.Col := c;
5347 end;
5349 procedure TMainForm.bbRemoveTextureClick(Sender: TObject);
5350 var
5351 a, i: Integer;
5352 begin
5353 i := lbTextureList.ItemIndex;
5354 if i = -1 then
5355 Exit;
5357 if MessageBox(0, PChar(Format(_lc[I_MSG_DEL_TEXTURE_PROMT],
5358 [SelectedTexture()])),
5359 PChar(_lc[I_MSG_DEL_TEXTURE]),
5360 MB_ICONQUESTION or MB_YESNO or
5361 MB_DEFBUTTON1) <> idYes then
5362 Exit;
5364 if gPanels <> nil then
5365 for a := 0 to High(gPanels) do
5366 if (gPanels[a].PanelType <> 0) and
5367 (gPanels[a].TextureName = SelectedTexture()) then
5368 begin
5369 ErrorMessageBox(_lc[I_MSG_DEL_TEXTURE_CANT]);
5370 Exit;
5371 end;
5373 g_DeleteTexture(SelectedTexture());
5374 i := slInvalidTextures.IndexOf(lbTextureList.Items[i]);
5375 if i > -1 then
5376 slInvalidTextures.Delete(i);
5377 if lbTextureList.ItemIndex > -1 then
5378 lbTextureList.Items.Delete(lbTextureList.ItemIndex)
5379 end;
5381 procedure TMainForm.aNewMapExecute(Sender: TObject);
5382 begin
5383 if (MessageBox(0, PChar(_lc[I_MSG_CLEAR_MAP_PROMT]),
5384 PChar(_lc[I_MSG_CLEAR_MAP]),
5385 MB_ICONQUESTION or MB_YESNO or
5386 MB_DEFBUTTON1) = mrYes) then
5387 FullClear();
5388 end;
5390 procedure TMainForm.aUndoExecute(Sender: TObject);
5391 var
5392 a: Integer;
5393 begin
5394 if UndoBuffer = nil then
5395 Exit;
5396 if UndoBuffer[High(UndoBuffer)] = nil then
5397 Exit;
5399 for a := 0 to High(UndoBuffer[High(UndoBuffer)]) do
5400 with UndoBuffer[High(UndoBuffer)][a] do
5401 begin
5402 case UndoType of
5403 UNDO_DELETE_PANEL:
5404 begin
5405 AddPanel(Panel^);
5406 Panel := nil;
5407 end;
5408 UNDO_DELETE_ITEM: AddItem(Item);
5409 UNDO_DELETE_AREA: AddArea(Area);
5410 UNDO_DELETE_MONSTER: AddMonster(Monster);
5411 UNDO_DELETE_TRIGGER: AddTrigger(Trigger);
5412 UNDO_ADD_PANEL: RemoveObject(AddID, OBJECT_PANEL);
5413 UNDO_ADD_ITEM: RemoveObject(AddID, OBJECT_ITEM);
5414 UNDO_ADD_AREA: RemoveObject(AddID, OBJECT_AREA);
5415 UNDO_ADD_MONSTER: RemoveObject(AddID, OBJECT_MONSTER);
5416 UNDO_ADD_TRIGGER: RemoveObject(AddID, OBJECT_TRIGGER);
5417 end;
5418 end;
5420 SetLength(UndoBuffer, Length(UndoBuffer)-1);
5422 RemoveSelectFromObjects();
5424 miUndo.Enabled := UndoBuffer <> nil;
5425 end;
5428 procedure TMainForm.aCopyObjectExecute(Sender: TObject);
5429 var
5430 a, b: Integer;
5431 CopyBuffer: TCopyRecArray;
5432 str: String;
5433 ok: Boolean;
5435 function CB_Compare(I1, I2: TCopyRec): Integer;
5436 begin
5437 Result := Integer(I1.ObjectType) - Integer(I2.ObjectType);
5439 if Result = 0 then // Одного типа
5440 Result := Integer(I1.ID) - Integer(I2.ID);
5441 end;
5443 procedure QuickSortCopyBuffer(L, R: Integer);
5444 var
5445 I, J: Integer;
5446 P, T: TCopyRec;
5447 begin
5448 repeat
5449 I := L;
5450 J := R;
5451 P := CopyBuffer[(L + R) shr 1];
5453 repeat
5454 while CB_Compare(CopyBuffer[I], P) < 0 do
5455 Inc(I);
5456 while CB_Compare(CopyBuffer[J], P) > 0 do
5457 Dec(J);
5459 if I <= J then
5460 begin
5461 T := CopyBuffer[I];
5462 CopyBuffer[I] := CopyBuffer[J];
5463 CopyBuffer[J] := T;
5464 Inc(I);
5465 Dec(J);
5466 end;
5467 until I > J;
5469 if L < J then
5470 QuickSortCopyBuffer(L, J);
5472 L := I;
5473 until I >= R;
5474 end;
5476 begin
5477 if SelectedObjects = nil then
5478 Exit;
5480 b := -1;
5481 CopyBuffer := nil;
5483 // Копируем объекты:
5484 for a := 0 to High(SelectedObjects) do
5485 if SelectedObjects[a].Live then
5486 with SelectedObjects[a] do
5487 begin
5488 SetLength(CopyBuffer, Length(CopyBuffer)+1);
5489 b := High(CopyBuffer);
5490 CopyBuffer[b].ID := ID;
5491 CopyBuffer[b].Panel := nil;
5493 case ObjectType of
5494 OBJECT_PANEL:
5495 begin
5496 CopyBuffer[b].ObjectType := OBJECT_PANEL;
5497 New(CopyBuffer[b].Panel);
5498 CopyBuffer[b].Panel^ := gPanels[ID];
5499 end;
5501 OBJECT_ITEM:
5502 begin
5503 CopyBuffer[b].ObjectType := OBJECT_ITEM;
5504 CopyBuffer[b].Item := gItems[ID];
5505 end;
5507 OBJECT_MONSTER:
5508 begin
5509 CopyBuffer[b].ObjectType := OBJECT_MONSTER;
5510 CopyBuffer[b].Monster := gMonsters[ID];
5511 end;
5513 OBJECT_AREA:
5514 begin
5515 CopyBuffer[b].ObjectType := OBJECT_AREA;
5516 CopyBuffer[b].Area := gAreas[ID];
5517 end;
5519 OBJECT_TRIGGER:
5520 begin
5521 CopyBuffer[b].ObjectType := OBJECT_TRIGGER;
5522 CopyBuffer[b].Trigger := gTriggers[ID];
5523 end;
5524 end;
5525 end;
5527 // Сортировка по ID:
5528 if CopyBuffer <> nil then
5529 begin
5530 QuickSortCopyBuffer(0, b);
5531 end;
5533 // Пестановка ссылок триггеров:
5534 for a := 0 to Length(CopyBuffer)-1 do
5535 if CopyBuffer[a].ObjectType = OBJECT_TRIGGER then
5536 begin
5537 case CopyBuffer[a].Trigger.TriggerType of
5538 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
5539 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
5540 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
5541 if CopyBuffer[a].Trigger.Data.PanelID <> -1 then
5542 begin
5543 ok := False;
5545 for b := 0 to Length(CopyBuffer)-1 do
5546 if (CopyBuffer[b].ObjectType = OBJECT_PANEL) and
5547 (Integer(CopyBuffer[b].ID) = CopyBuffer[a].Trigger.Data.PanelID) then
5548 begin
5549 CopyBuffer[a].Trigger.Data.PanelID := b;
5550 ok := True;
5551 Break;
5552 end;
5554 // Этих панелей нет среди копируемых:
5555 if not ok then
5556 CopyBuffer[a].Trigger.Data.PanelID := -1;
5557 end;
5559 TRIGGER_PRESS, TRIGGER_ON,
5560 TRIGGER_OFF, TRIGGER_ONOFF:
5561 if CopyBuffer[a].Trigger.Data.MonsterID <> 0 then
5562 begin
5563 ok := False;
5565 for b := 0 to Length(CopyBuffer)-1 do
5566 if (CopyBuffer[b].ObjectType = OBJECT_MONSTER) and
5567 (Integer(CopyBuffer[b].ID) = CopyBuffer[a].Trigger.Data.MonsterID-1) then
5568 begin
5569 CopyBuffer[a].Trigger.Data.MonsterID := b+1;
5570 ok := True;
5571 Break;
5572 end;
5574 // Этих монстров нет среди копируемых:
5575 if not ok then
5576 CopyBuffer[a].Trigger.Data.MonsterID := 0;
5577 end;
5579 TRIGGER_SHOT:
5580 if CopyBuffer[a].Trigger.Data.ShotPanelID <> -1 then
5581 begin
5582 ok := False;
5584 for b := 0 to Length(CopyBuffer)-1 do
5585 if (CopyBuffer[b].ObjectType = OBJECT_PANEL) and
5586 (Integer(CopyBuffer[b].ID) = CopyBuffer[a].Trigger.Data.ShotPanelID) then
5587 begin
5588 CopyBuffer[a].Trigger.Data.ShotPanelID := b;
5589 ok := True;
5590 Break;
5591 end;
5593 // Этих панелей нет среди копируемых:
5594 if not ok then
5595 CopyBuffer[a].Trigger.Data.ShotPanelID := -1;
5596 end;
5597 end;
5599 if CopyBuffer[a].Trigger.TexturePanel <> -1 then
5600 begin
5601 ok := False;
5603 for b := 0 to Length(CopyBuffer)-1 do
5604 if (CopyBuffer[b].ObjectType = OBJECT_PANEL) and
5605 (Integer(CopyBuffer[b].ID) = CopyBuffer[a].Trigger.TexturePanel) then
5606 begin
5607 CopyBuffer[a].Trigger.TexturePanel := b;
5608 ok := True;
5609 Break;
5610 end;
5612 // Этих панелей нет среди копируемых:
5613 if not ok then
5614 CopyBuffer[a].Trigger.TexturePanel := -1;
5615 end;
5616 end;
5618 // В буфер обмена:
5619 str := CopyBufferToString(CopyBuffer);
5620 ClipBoard.AsText := str;
5622 for a := 0 to Length(CopyBuffer)-1 do
5623 if (CopyBuffer[a].ObjectType = OBJECT_PANEL) and
5624 (CopyBuffer[a].Panel <> nil) then
5625 Dispose(CopyBuffer[a].Panel);
5627 CopyBuffer := nil;
5628 end;
5630 procedure TMainForm.aPasteObjectExecute(Sender: TObject);
5631 var
5632 a, h: Integer;
5633 CopyBuffer: TCopyRecArray;
5634 res, rel: Boolean;
5635 swad, ssec, sres: String;
5636 NoTextureID: DWORD;
5637 pmin: TPoint;
5638 begin
5639 CopyBuffer := nil;
5640 NoTextureID := 0;
5641 pmin.X := High(pmin.X);
5642 pmin.Y := High(pmin.Y);
5644 StringToCopyBuffer(ClipBoard.AsText, CopyBuffer, pmin);
5645 rel := not(ssShift in GetKeyShiftState());
5647 if CopyBuffer = nil then
5648 Exit;
5650 RemoveSelectFromObjects();
5652 h := High(CopyBuffer);
5653 for a := 0 to h do
5654 with CopyBuffer[a] do
5655 begin
5656 case ObjectType of
5657 OBJECT_PANEL:
5658 if Panel <> nil then
5659 begin
5660 if rel then
5661 begin
5662 Panel^.X := Panel^.X - pmin.X - MapOffset.X + 32;
5663 Panel^.Y := Panel^.Y - pmin.Y - MapOffset.Y + 32;
5664 end;
5666 Panel^.TextureID := TEXTURE_SPECIAL_NONE;
5667 Panel^.TextureWidth := 1;
5668 Panel^.TextureHeight := 1;
5670 if (Panel^.PanelType = PANEL_LIFTUP) or
5671 (Panel^.PanelType = PANEL_LIFTDOWN) or
5672 (Panel^.PanelType = PANEL_LIFTLEFT) or
5673 (Panel^.PanelType = PANEL_LIFTRIGHT) or
5674 (Panel^.PanelType = PANEL_BLOCKMON) or
5675 (Panel^.TextureName = '') then
5676 begin // Нет или не может быть текстуры:
5677 end
5678 else // Есть текстура:
5679 begin
5680 // Обычная текстура:
5681 if not IsSpecialTexture(Panel^.TextureName) then
5682 begin
5683 res := g_GetTexture(Panel^.TextureName, Panel^.TextureID);
5685 if not res then
5686 begin
5687 g_ProcessResourceStr(Panel^.TextureName, swad, ssec, sres);
5688 AddTexture(swad, ssec, sres, True);
5689 res := g_GetTexture(Panel^.TextureName, Panel^.TextureID);
5690 end;
5692 if res then
5693 g_GetTextureSizeByName(Panel^.TextureName,
5694 Panel^.TextureWidth, Panel^.TextureHeight)
5695 else
5696 if g_GetTexture('NOTEXTURE', NoTextureID) then
5697 begin
5698 Panel^.TextureID := TEXTURE_SPECIAL_NOTEXTURE;
5699 g_GetTextureSizeByID(NoTextureID, Panel^.TextureWidth, Panel^.TextureHeight);
5700 end;
5701 end
5702 else // Спец.текстура:
5703 begin
5704 Panel^.TextureID := SpecialTextureID(Panel^.TextureName);
5705 with MainForm.lbTextureList.Items do
5706 if IndexOf(Panel^.TextureName) = -1 then
5707 Add(Panel^.TextureName);
5708 end;
5709 end;
5711 ID := AddPanel(Panel^);
5712 Dispose(Panel);
5713 Undo_Add(OBJECT_PANEL, ID, a > 0);
5714 SelectObject(OBJECT_PANEL, ID, True);
5715 end;
5717 OBJECT_ITEM:
5718 begin
5719 if rel then
5720 begin
5721 Item.X := Item.X - pmin.X - MapOffset.X + 32;
5722 Item.Y := Item.Y - pmin.Y - MapOffset.Y + 32;
5723 end;
5725 ID := AddItem(Item);
5726 Undo_Add(OBJECT_ITEM, ID, a > 0);
5727 SelectObject(OBJECT_ITEM, ID, True);
5728 end;
5730 OBJECT_MONSTER:
5731 begin
5732 if rel then
5733 begin
5734 Monster.X := Monster.X - pmin.X - MapOffset.X + 32;
5735 Monster.Y := Monster.Y - pmin.Y - MapOffset.Y + 32;
5736 end;
5738 ID := AddMonster(Monster);
5739 Undo_Add(OBJECT_MONSTER, ID, a > 0);
5740 SelectObject(OBJECT_MONSTER, ID, True);
5741 end;
5743 OBJECT_AREA:
5744 begin
5745 if rel then
5746 begin
5747 Area.X := Area.X - pmin.X - MapOffset.X + 32;
5748 Area.Y := Area.Y - pmin.Y - MapOffset.Y + 32;
5749 end;
5751 ID := AddArea(Area);
5752 Undo_Add(OBJECT_AREA, ID, a > 0);
5753 SelectObject(OBJECT_AREA, ID, True);
5754 end;
5756 OBJECT_TRIGGER:
5757 begin
5758 if rel then
5759 with Trigger do
5760 begin
5761 X := X - pmin.X - MapOffset.X + 32;
5762 Y := Y - pmin.Y - MapOffset.Y + 32;
5764 case TriggerType of
5765 TRIGGER_TELEPORT:
5766 begin
5767 Data.TargetPoint.X :=
5768 Data.TargetPoint.X - pmin.X - MapOffset.X + 32;
5769 Data.TargetPoint.Y :=
5770 Data.TargetPoint.Y - pmin.Y - MapOffset.Y + 32;
5771 end;
5772 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
5773 begin
5774 Data.tX := Data.tX - pmin.X - MapOffset.X + 32;
5775 Data.tY := Data.tY - pmin.Y - MapOffset.Y + 32;
5776 end;
5777 TRIGGER_SPAWNMONSTER:
5778 begin
5779 Data.MonPos.X :=
5780 Data.MonPos.X - pmin.X - MapOffset.X + 32;
5781 Data.MonPos.Y :=
5782 Data.MonPos.Y - pmin.Y - MapOffset.Y + 32;
5783 end;
5784 TRIGGER_SPAWNITEM:
5785 begin
5786 Data.ItemPos.X :=
5787 Data.ItemPos.X - pmin.X - MapOffset.X + 32;
5788 Data.ItemPos.Y :=
5789 Data.ItemPos.Y - pmin.Y - MapOffset.Y + 32;
5790 end;
5791 TRIGGER_SHOT:
5792 begin
5793 Data.ShotPos.X :=
5794 Data.ShotPos.X - pmin.X - MapOffset.X + 32;
5795 Data.ShotPos.Y :=
5796 Data.ShotPos.Y - pmin.Y - MapOffset.Y + 32;
5797 end;
5798 end;
5799 end;
5801 ID := AddTrigger(Trigger);
5802 Undo_Add(OBJECT_TRIGGER, ID, a > 0);
5803 SelectObject(OBJECT_TRIGGER, ID, True);
5804 end;
5805 end;
5806 end;
5808 // Переставляем ссылки триггеров:
5809 for a := 0 to High(CopyBuffer) do
5810 if CopyBuffer[a].ObjectType = OBJECT_TRIGGER then
5811 begin
5812 case CopyBuffer[a].Trigger.TriggerType of
5813 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
5814 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
5815 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
5816 if CopyBuffer[a].Trigger.Data.PanelID <> -1 then
5817 gTriggers[CopyBuffer[a].ID].Data.PanelID :=
5818 CopyBuffer[CopyBuffer[a].Trigger.Data.PanelID].ID;
5820 TRIGGER_PRESS, TRIGGER_ON,
5821 TRIGGER_OFF, TRIGGER_ONOFF:
5822 if CopyBuffer[a].Trigger.Data.MonsterID <> 0 then
5823 gTriggers[CopyBuffer[a].ID].Data.MonsterID :=
5824 CopyBuffer[CopyBuffer[a].Trigger.Data.MonsterID-1].ID+1;
5826 TRIGGER_SHOT:
5827 if CopyBuffer[a].Trigger.Data.ShotPanelID <> -1 then
5828 gTriggers[CopyBuffer[a].ID].Data.ShotPanelID :=
5829 CopyBuffer[CopyBuffer[a].Trigger.Data.ShotPanelID].ID;
5830 end;
5832 if CopyBuffer[a].Trigger.TexturePanel <> -1 then
5833 gTriggers[CopyBuffer[a].ID].TexturePanel :=
5834 CopyBuffer[CopyBuffer[a].Trigger.TexturePanel].ID;
5835 end;
5837 CopyBuffer := nil;
5839 if h = 0 then
5840 FillProperty();
5841 end;
5843 procedure TMainForm.aCutObjectExecute(Sender: TObject);
5844 begin
5845 miCopy.Click();
5846 DeleteSelectedObjects();
5847 end;
5849 procedure TMainForm.vleObjectPropertyEditButtonClick(Sender: TObject);
5850 var
5851 Key, FileName: String;
5852 b: Byte;
5853 begin
5854 Key := vleObjectProperty.Keys[vleObjectProperty.Row];
5856 if Key = _lc[I_PROP_PANEL_TYPE] then
5857 begin
5858 with ChooseTypeForm, vleObjectProperty do
5859 begin // Выбор типа панели:
5860 Caption := _lc[I_PROP_PANEL_TYPE];
5861 lbTypeSelect.Items.Clear();
5863 for b := 0 to High(PANELNAMES) do
5864 begin
5865 lbTypeSelect.Items.Add(PANELNAMES[b]);
5866 if Values[Key] = PANELNAMES[b] then
5867 lbTypeSelect.ItemIndex := b;
5868 end;
5870 if ShowModal() = mrOK then
5871 begin
5872 b := lbTypeSelect.ItemIndex;
5873 Values[Key] := PANELNAMES[b];
5874 vleObjectPropertyApply(Sender);
5875 end;
5876 end
5877 end
5878 else if Key = _lc[I_PROP_TR_TELEPORT_TO] then
5879 SelectFlag := SELECTFLAG_TELEPORT
5880 else if Key = _lc[I_PROP_TR_SPAWN_TO] then
5881 SelectFlag := SELECTFLAG_SPAWNPOINT
5882 else if (Key = _lc[I_PROP_TR_DOOR_PANEL]) or
5883 (Key = _lc[I_PROP_TR_TRAP_PANEL]) then
5884 SelectFlag := SELECTFLAG_DOOR
5885 else if Key = _lc[I_PROP_TR_TEXTURE_PANEL] then
5886 begin
5887 DrawPressRect := False;
5888 SelectFlag := SELECTFLAG_TEXTURE;
5889 end
5890 else if Key = _lc[I_PROP_TR_SHOT_PANEL] then
5891 SelectFlag := SELECTFLAG_SHOTPANEL
5892 else if Key = _lc[I_PROP_TR_LIFT_PANEL] then
5893 SelectFlag := SELECTFLAG_LIFT
5894 else if key = _lc[I_PROP_TR_EX_MONSTER] then
5895 SelectFlag := SELECTFLAG_MONSTER
5896 else if Key = _lc[I_PROP_TR_EX_AREA] then
5897 begin
5898 SelectFlag := SELECTFLAG_NONE;
5899 DrawPressRect := True;
5900 end
5901 else if Key = _lc[I_PROP_TR_NEXT_MAP] then
5902 begin // Выбор следующей карты:
5903 g_ProcessResourceStr(OpenedMap, @FileName, nil, nil);
5904 SelectMapForm.Caption := _lc[I_CAP_SELECT];
5905 SelectMapForm.GetMaps(FileName);
5907 if SelectMapForm.ShowModal() = mrOK then
5908 begin
5909 vleObjectProperty.Values[Key] := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
5910 vleObjectPropertyApply(Sender);
5911 end;
5912 end
5913 else if (Key = _lc[I_PROP_TR_SOUND_NAME]) or
5914 (Key = _lc[I_PROP_TR_MUSIC_NAME]) then
5915 begin // Выбор файла звука/музыки:
5916 AddSoundForm.OKFunction := nil;
5917 AddSoundForm.lbResourcesList.MultiSelect := False;
5918 AddSoundForm.SetResource := vleObjectProperty.Values[Key];
5920 if (AddSoundForm.ShowModal() = mrOk) then
5921 begin
5922 vleObjectProperty.Values[Key] := AddSoundForm.ResourceName;
5923 vleObjectPropertyApply(Sender);
5924 end;
5925 end
5926 else if Key = _lc[I_PROP_TR_ACTIVATION] then
5927 with ActivationTypeForm, vleObjectProperty do
5928 begin // Выбор типов активации:
5929 cbPlayerCollide.Checked := Pos('PC', Values[Key]) > 0;
5930 cbMonsterCollide.Checked := Pos('MC', Values[Key]) > 0;
5931 cbPlayerPress.Checked := Pos('PP', Values[Key]) > 0;
5932 cbMonsterPress.Checked := Pos('MP', Values[Key]) > 0;
5933 cbShot.Checked := Pos('SH', Values[Key]) > 0;
5934 cbNoMonster.Checked := Pos('NM', Values[Key]) > 0;
5936 if ShowModal() = mrOK then
5937 begin
5938 b := 0;
5939 if cbPlayerCollide.Checked then
5940 b := ACTIVATE_PLAYERCOLLIDE;
5941 if cbMonsterCollide.Checked then
5942 b := b or ACTIVATE_MONSTERCOLLIDE;
5943 if cbPlayerPress.Checked then
5944 b := b or ACTIVATE_PLAYERPRESS;
5945 if cbMonsterPress.Checked then
5946 b := b or ACTIVATE_MONSTERPRESS;
5947 if cbShot.Checked then
5948 b := b or ACTIVATE_SHOT;
5949 if cbNoMonster.Checked then
5950 b := b or ACTIVATE_NOMONSTER;
5952 Values[Key] := ActivateToStr(b);
5953 vleObjectPropertyApply(Sender);
5954 end;
5955 end
5956 else if Key = _lc[I_PROP_TR_KEYS] then
5957 with KeysForm, vleObjectProperty do
5958 begin // Выбор необходимых ключей:
5959 cbRedKey.Checked := Pos('RK', Values[Key]) > 0;
5960 cbGreenKey.Checked := Pos('GK', Values[Key]) > 0;
5961 cbBlueKey.Checked := Pos('BK', Values[Key]) > 0;
5962 cbRedTeam.Checked := Pos('RT', Values[Key]) > 0;
5963 cbBlueTeam.Checked := Pos('BT', Values[Key]) > 0;
5965 if ShowModal() = mrOK then
5966 begin
5967 b := 0;
5968 if cbRedKey.Checked then
5969 b := KEY_RED;
5970 if cbGreenKey.Checked then
5971 b := b or KEY_GREEN;
5972 if cbBlueKey.Checked then
5973 b := b or KEY_BLUE;
5974 if cbRedTeam.Checked then
5975 b := b or KEY_REDTEAM;
5976 if cbBlueTeam.Checked then
5977 b := b or KEY_BLUETEAM;
5979 Values[Key] := KeyToStr(b);
5980 vleObjectPropertyApply(Sender);
5981 end;
5982 end
5983 else if Key = _lc[I_PROP_TR_FX_TYPE] then
5984 with ChooseTypeForm, vleObjectProperty do
5985 begin // Выбор типа эффекта:
5986 Caption := _lc[I_CAP_FX_TYPE];
5987 lbTypeSelect.Items.Clear();
5989 for b := EFFECT_NONE to EFFECT_FIRE do
5990 lbTypeSelect.Items.Add(EffectToStr(b));
5992 lbTypeSelect.ItemIndex := StrToEffect(Values[Key]);
5994 if ShowModal() = mrOK then
5995 begin
5996 b := lbTypeSelect.ItemIndex;
5997 Values[Key] := EffectToStr(b);
5998 vleObjectPropertyApply(Sender);
5999 end;
6000 end
6001 else if Key = _lc[I_PROP_TR_MONSTER_TYPE] then
6002 with ChooseTypeForm, vleObjectProperty do
6003 begin // Выбор типа монстра:
6004 Caption := _lc[I_CAP_MONSTER_TYPE];
6005 lbTypeSelect.Items.Clear();
6007 for b := MONSTER_DEMON to MONSTER_MAN do
6008 lbTypeSelect.Items.Add(MonsterToStr(b));
6010 lbTypeSelect.ItemIndex := StrToMonster(Values[Key]) - MONSTER_DEMON;
6012 if ShowModal() = mrOK then
6013 begin
6014 b := lbTypeSelect.ItemIndex + MONSTER_DEMON;
6015 Values[Key] := MonsterToStr(b);
6016 vleObjectPropertyApply(Sender);
6017 end;
6018 end
6019 else if Key = _lc[I_PROP_TR_ITEM_TYPE] then
6020 with ChooseTypeForm, vleObjectProperty do
6021 begin // Выбор типа предмета:
6022 Caption := _lc[I_CAP_ITEM_TYPE];
6023 lbTypeSelect.Items.Clear();
6025 for b := ITEM_MEDKIT_SMALL to ITEM_KEY_BLUE do
6026 lbTypeSelect.Items.Add(ItemToStr(b));
6027 lbTypeSelect.Items.Add(ItemToStr(ITEM_BOTTLE));
6028 lbTypeSelect.Items.Add(ItemToStr(ITEM_HELMET));
6029 lbTypeSelect.Items.Add(ItemToStr(ITEM_JETPACK));
6030 lbTypeSelect.Items.Add(ItemToStr(ITEM_INVIS));
6031 lbTypeSelect.Items.Add(ItemToStr(ITEM_WEAPON_FLAMETHROWER));
6032 lbTypeSelect.Items.Add(ItemToStr(ITEM_AMMO_FUELCAN));
6034 b := StrToItem(Values[Key]);
6035 if b >= ITEM_BOTTLE then
6036 b := b - 2;
6037 lbTypeSelect.ItemIndex := b - ITEM_MEDKIT_SMALL;
6039 if ShowModal() = mrOK then
6040 begin
6041 b := lbTypeSelect.ItemIndex + ITEM_MEDKIT_SMALL;
6042 if b >= ITEM_WEAPON_KASTET then
6043 b := b + 2;
6044 Values[Key] := ItemToStr(b);
6045 vleObjectPropertyApply(Sender);
6046 end;
6047 end
6048 else if Key = _lc[I_PROP_TR_SHOT_TYPE] then
6049 with ChooseTypeForm, vleObjectProperty do
6050 begin // Выбор типа предмета:
6051 Caption := _lc[I_PROP_TR_SHOT_TYPE];
6052 lbTypeSelect.Items.Clear();
6054 for b := TRIGGER_SHOT_PISTOL to TRIGGER_SHOT_MAX do
6055 lbTypeSelect.Items.Add(ShotToStr(b));
6057 lbTypeSelect.ItemIndex := StrToShot(Values[Key]);
6059 if ShowModal() = mrOK then
6060 begin
6061 b := lbTypeSelect.ItemIndex;
6062 Values[Key] := ShotToStr(b);
6063 vleObjectPropertyApply(Sender);
6064 end;
6065 end
6066 else if Key = _lc[I_PROP_TR_EFFECT_TYPE] then
6067 with ChooseTypeForm, vleObjectProperty do
6068 begin // Выбор типа эффекта:
6069 Caption := _lc[I_CAP_FX_TYPE];
6070 lbTypeSelect.Items.Clear();
6072 lbTypeSelect.Items.Add(_lc[I_PROP_TR_EFFECT_PARTICLE]);
6073 lbTypeSelect.Items.Add(_lc[I_PROP_TR_EFFECT_ANIMATION]);
6074 if Values[Key] = _lc[I_PROP_TR_EFFECT_ANIMATION] then
6075 lbTypeSelect.ItemIndex := 1
6076 else
6077 lbTypeSelect.ItemIndex := 0;
6079 if ShowModal() = mrOK then
6080 begin
6081 b := lbTypeSelect.ItemIndex;
6082 if b = 0 then
6083 Values[Key] := _lc[I_PROP_TR_EFFECT_PARTICLE]
6084 else
6085 Values[Key] := _lc[I_PROP_TR_EFFECT_ANIMATION];
6086 vleObjectPropertyApply(Sender);
6087 end;
6088 end
6089 else if Key = _lc[I_PROP_TR_EFFECT_SUBTYPE] then
6090 with ChooseTypeForm, vleObjectProperty do
6091 begin // Выбор подтипа эффекта:
6092 Caption := _lc[I_CAP_FX_TYPE];
6093 lbTypeSelect.Items.Clear();
6095 if Values[_lc[I_PROP_TR_EFFECT_TYPE]] = _lc[I_PROP_TR_EFFECT_ANIMATION] then
6096 begin
6097 for b := EFFECT_TELEPORT to EFFECT_FIRE do
6098 lbTypeSelect.Items.Add(EffectToStr(b));
6100 lbTypeSelect.ItemIndex := StrToEffect(Values[Key]) - 1;
6101 end else
6102 begin
6103 lbTypeSelect.Items.Add(_lc[I_PROP_TR_EFFECT_SLIQUID]);
6104 lbTypeSelect.Items.Add(_lc[I_PROP_TR_EFFECT_LLIQUID]);
6105 lbTypeSelect.Items.Add(_lc[I_PROP_TR_EFFECT_DLIQUID]);
6106 lbTypeSelect.Items.Add(_lc[I_PROP_TR_EFFECT_BLOOD]);
6107 lbTypeSelect.Items.Add(_lc[I_PROP_TR_EFFECT_SPARK]);
6108 lbTypeSelect.Items.Add(_lc[I_PROP_TR_EFFECT_BUBBLE]);
6109 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_SLIQUID;
6110 if Values[Key] = _lc[I_PROP_TR_EFFECT_LLIQUID] then
6111 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_LLIQUID;
6112 if Values[Key] = _lc[I_PROP_TR_EFFECT_DLIQUID] then
6113 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_DLIQUID;
6114 if Values[Key] = _lc[I_PROP_TR_EFFECT_BLOOD] then
6115 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_BLOOD;
6116 if Values[Key] = _lc[I_PROP_TR_EFFECT_SPARK] then
6117 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_SPARK;
6118 if Values[Key] = _lc[I_PROP_TR_EFFECT_BUBBLE] then
6119 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_BUBBLE;
6120 end;
6122 if ShowModal() = mrOK then
6123 begin
6124 b := lbTypeSelect.ItemIndex;
6126 if Values[_lc[I_PROP_TR_EFFECT_TYPE]] = _lc[I_PROP_TR_EFFECT_ANIMATION] then
6127 Values[Key] := EffectToStr(b + 1)
6128 else begin
6129 Values[Key] := _lc[I_PROP_TR_EFFECT_SLIQUID];
6130 if b = TRIGGER_EFFECT_LLIQUID then
6131 Values[Key] := _lc[I_PROP_TR_EFFECT_LLIQUID];
6132 if b = TRIGGER_EFFECT_DLIQUID then
6133 Values[Key] := _lc[I_PROP_TR_EFFECT_DLIQUID];
6134 if b = TRIGGER_EFFECT_BLOOD then
6135 Values[Key] := _lc[I_PROP_TR_EFFECT_BLOOD];
6136 if b = TRIGGER_EFFECT_SPARK then
6137 Values[Key] := _lc[I_PROP_TR_EFFECT_SPARK];
6138 if b = TRIGGER_EFFECT_BUBBLE then
6139 Values[Key] := _lc[I_PROP_TR_EFFECT_BUBBLE];
6140 end;
6142 vleObjectPropertyApply(Sender);
6143 end;
6144 end
6145 else if Key = _lc[I_PROP_TR_EFFECT_COLOR] then
6146 with vleObjectProperty do
6147 begin // Выбор цвета эффекта:
6148 ColorDialog.Color := StrToIntDef(Values[Key], 0);
6149 if ColorDialog.Execute then
6150 begin
6151 Values[Key] := IntToStr(ColorDialog.Color);
6152 vleObjectPropertyApply(Sender);
6153 end;
6154 end
6155 else if Key = _lc[I_PROP_PANEL_TEX] then
6156 begin // Смена текстуры:
6157 vleObjectProperty.Values[Key] := SelectedTexture();
6158 vleObjectPropertyApply(Sender);
6159 end;
6160 end;
6162 procedure TMainForm.vleObjectPropertyApply(Sender: TObject);
6163 begin
6164 // hack to prevent empty ID in list
6165 RenderPanel.SetFocus();
6166 bApplyProperty.Click();
6167 vleObjectProperty.SetFocus();
6168 end;
6170 procedure TMainForm.aSaveMapExecute(Sender: TObject);
6171 var
6172 FileName, Section, Res: String;
6173 begin
6174 if OpenedMap = '' then
6175 begin
6176 aSaveMapAsExecute(nil);
6177 Exit;
6178 end;
6180 g_ProcessResourceStr(OpenedMap, FileName, Section, Res);
6182 SaveMap(FileName+':\'+Res);
6183 end;
6185 procedure TMainForm.aOpenMapExecute(Sender: TObject);
6186 begin
6187 OpenDialog.Filter := _lc[I_FILE_FILTER_ALL];
6189 if OpenDialog.Execute() then
6190 begin
6191 OpenMapFile(OpenDialog.FileName);
6192 OpenDialog.InitialDir := ExtractFileDir(OpenDialog.FileName);
6193 end;
6194 end;
6196 procedure TMainForm.OpenMapFile(FileName: String);
6197 begin
6198 if (Pos('.ini', LowerCase(ExtractFileName(FileName))) > 0) then
6199 begin // INI карты:
6200 FullClear();
6202 pLoadProgress.Left := (RenderPanel.Width div 2)-(pLoadProgress.Width div 2);
6203 pLoadProgress.Top := (RenderPanel.Height div 2)-(pLoadProgress.Height div 2);
6204 pLoadProgress.Show();
6206 OpenedMap := '';
6207 OpenedWAD := '';
6209 LoadMapOld(FileName);
6211 MainForm.Caption := Format('%s - %s', [FormCaption, ExtractFileName(FileName)]);
6213 pLoadProgress.Hide();
6214 MainForm.FormResize(Self);
6215 end
6216 else // Карты из WAD:
6217 begin
6218 OpenMap(FileName, '');
6219 end;
6220 end;
6222 procedure TMainForm.FormActivate(Sender: TObject);
6223 var
6224 lang: Integer;
6225 config: TConfig;
6226 begin
6227 MainForm.ActiveControl := RenderPanel;
6229 // Язык:
6230 if gLanguage = '' then
6231 begin
6232 lang := SelectLanguageForm.ShowModal();
6233 case lang of
6234 1: gLanguage := LANGUAGE_ENGLISH;
6235 else gLanguage := LANGUAGE_RUSSIAN;
6236 end;
6238 config := TConfig.CreateFile(EditorDir+'Editor.cfg');
6239 config.WriteStr('Editor', 'Language', gLanguage);
6240 config.SaveFile(EditorDir+'Editor.cfg');
6241 config.Free();
6242 end;
6244 //e_WriteLog('Read language file', MSG_NOTIFY);
6245 //g_Language_Load(EditorDir+'\data\'+gLanguage+LANGUAGE_FILE_NAME);
6246 g_Language_Set(gLanguage);
6247 end;
6249 procedure TMainForm.aDeleteMap(Sender: TObject);
6250 var
6251 res: Integer;
6252 FileName: String;
6253 MapName: String;
6254 begin
6255 OpenDialog.Filter := _lc[I_FILE_FILTER_WAD];
6257 if not OpenDialog.Execute() then
6258 Exit;
6260 FileName := OpenDialog.FileName;
6261 SelectMapForm.Caption := _lc[I_CAP_REMOVE];
6262 SelectMapForm.lbMapList.Items.Clear();
6263 SelectMapForm.GetMaps(FileName);
6265 if SelectMapForm.ShowModal() <> mrOK then
6266 Exit;
6268 MapName := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
6269 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
6270 Exit;
6272 g_DeleteResource(FileName, '', MapName, res);
6273 if res <> 0 then
6274 begin
6275 MessageBox(0, PChar('Cant delete map res=' + IntToStr(res)), PChar('Map not deleted!'), MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1);
6276 Exit
6277 end;
6279 MessageBox(
6280 0,
6281 PChar(Format(_lc[I_MSG_MAP_DELETED_PROMT], [MapName])),
6282 PChar(_lc[I_MSG_MAP_DELETED]),
6283 MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1
6284 );
6286 // Удалили текущую карту - сохранять по старому ее нельзя:
6287 if OpenedMap = (FileName + ':\' + MapName) then
6288 begin
6289 OpenedMap := '';
6290 OpenedWAD := '';
6291 MainForm.Caption := FormCaption
6292 end
6293 end;
6295 procedure TMainForm.vleObjectPropertyKeyDown(Sender: TObject;
6296 var Key: Word; Shift: TShiftState);
6297 begin
6298 if Key = VK_RETURN then
6299 vleObjectPropertyApply(Sender);
6300 end;
6302 procedure MovePanel(var ID: DWORD; MoveType: Byte);
6303 var
6304 _id, a: Integer;
6305 tmp: TPanel;
6306 begin
6307 if (ID = 0) and (MoveType = 0) then
6308 Exit;
6309 if (ID = DWORD(High(gPanels))) and (MoveType <> 0) then
6310 Exit;
6311 if (ID > DWORD(High(gPanels))) then
6312 Exit;
6314 _id := Integer(ID);
6316 if MoveType = 0 then // to Back
6317 begin
6318 if gTriggers <> nil then
6319 for a := 0 to High(gTriggers) do
6320 with gTriggers[a] do
6321 begin
6322 if TriggerType = TRIGGER_NONE then
6323 Continue;
6325 if TexturePanel = _id then
6326 TexturePanel := 0
6327 else
6328 if (TexturePanel >= 0) and (TexturePanel < _id) then
6329 Inc(TexturePanel);
6331 case TriggerType of
6332 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
6333 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
6334 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
6335 if Data.PanelID = _id then
6336 Data.PanelID := 0
6337 else
6338 if (Data.PanelID >= 0) and (Data.PanelID < _id) then
6339 Inc(Data.PanelID);
6341 TRIGGER_SHOT:
6342 if Data.ShotPanelID = _id then
6343 Data.ShotPanelID := 0
6344 else
6345 if (Data.ShotPanelID >= 0) and (Data.ShotPanelID < _id) then
6346 Inc(Data.ShotPanelID);
6347 end;
6348 end;
6350 tmp := gPanels[_id];
6352 for a := _id downto 1 do
6353 gPanels[a] := gPanels[a-1];
6355 gPanels[0] := tmp;
6357 ID := 0;
6358 end
6359 else // to Front
6360 begin
6361 if gTriggers <> nil then
6362 for a := 0 to High(gTriggers) do
6363 with gTriggers[a] do
6364 begin
6365 if TriggerType = TRIGGER_NONE then
6366 Continue;
6368 if TexturePanel = _id then
6369 TexturePanel := High(gPanels)
6370 else
6371 if TexturePanel > _id then
6372 Dec(TexturePanel);
6374 case TriggerType of
6375 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
6376 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
6377 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
6378 if Data.PanelID = _id then
6379 Data.PanelID := High(gPanels)
6380 else
6381 if Data.PanelID > _id then
6382 Dec(Data.PanelID);
6384 TRIGGER_SHOT:
6385 if Data.ShotPanelID = _id then
6386 Data.ShotPanelID := High(gPanels)
6387 else
6388 if Data.ShotPanelID > _id then
6389 Dec(Data.ShotPanelID);
6390 end;
6391 end;
6393 tmp := gPanels[_id];
6395 for a := _id to High(gPanels)-1 do
6396 gPanels[a] := gPanels[a+1];
6398 gPanels[High(gPanels)] := tmp;
6400 ID := High(gPanels);
6401 end;
6402 end;
6404 procedure TMainForm.aMoveToBack(Sender: TObject);
6405 var
6406 a: Integer;
6407 begin
6408 if SelectedObjects = nil then
6409 Exit;
6411 for a := 0 to High(SelectedObjects) do
6412 with SelectedObjects[a] do
6413 if Live and (ObjectType = OBJECT_PANEL) then
6414 begin
6415 SelectedObjects[0] := SelectedObjects[a];
6416 SetLength(SelectedObjects, 1);
6417 MovePanel(ID, 0);
6418 FillProperty();
6419 Break;
6420 end;
6421 end;
6423 procedure TMainForm.aMoveToFore(Sender: TObject);
6424 var
6425 a: Integer;
6426 begin
6427 if SelectedObjects = nil then
6428 Exit;
6430 for a := 0 to High(SelectedObjects) do
6431 with SelectedObjects[a] do
6432 if Live and (ObjectType = OBJECT_PANEL) then
6433 begin
6434 SelectedObjects[0] := SelectedObjects[a];
6435 SetLength(SelectedObjects, 1);
6436 MovePanel(ID, 1);
6437 FillProperty();
6438 Break;
6439 end;
6440 end;
6442 procedure TMainForm.aSaveMapAsExecute(Sender: TObject);
6443 var
6444 idx: Integer;
6445 begin
6446 SaveDialog.Filter := _lc[I_FILE_FILTER_WAD];
6448 if not SaveDialog.Execute() then
6449 Exit;
6451 SaveMapForm.GetMaps(SaveDialog.FileName, True);
6453 if SaveMapForm.ShowModal() <> mrOK then
6454 Exit;
6456 SaveDialog.InitialDir := ExtractFileDir(SaveDialog.FileName);
6457 OpenedMap := SaveDialog.FileName+':\'+SaveMapForm.eMapName.Text;
6458 OpenedWAD := SaveDialog.FileName;
6460 idx := RecentFiles.IndexOf(OpenedMap);
6461 // Такая карта уже недавно открывалась:
6462 if idx >= 0 then
6463 RecentFiles.Delete(idx);
6464 RecentFiles.Insert(0, OpenedMap);
6465 RefreshRecentMenu;
6467 SaveMap(OpenedMap);
6469 gMapInfo.FileName := SaveDialog.FileName;
6470 gMapInfo.MapName := SaveMapForm.eMapName.Text;
6471 UpdateCaption(gMapInfo.Name, ExtractFileName(gMapInfo.FileName), gMapInfo.MapName);
6472 end;
6474 procedure TMainForm.aSelectAllExecute(Sender: TObject);
6475 var
6476 a: Integer;
6477 begin
6478 RemoveSelectFromObjects();
6480 case pcObjects.ActivePageIndex+1 of
6481 OBJECT_PANEL:
6482 if gPanels <> nil then
6483 for a := 0 to High(gPanels) do
6484 if gPanels[a].PanelType <> PANEL_NONE then
6485 SelectObject(OBJECT_PANEL, a, True);
6486 OBJECT_ITEM:
6487 if gItems <> nil then
6488 for a := 0 to High(gItems) do
6489 if gItems[a].ItemType <> ITEM_NONE then
6490 SelectObject(OBJECT_ITEM, a, True);
6491 OBJECT_MONSTER:
6492 if gMonsters <> nil then
6493 for a := 0 to High(gMonsters) do
6494 if gMonsters[a].MonsterType <> MONSTER_NONE then
6495 SelectObject(OBJECT_MONSTER, a, True);
6496 OBJECT_AREA:
6497 if gAreas <> nil then
6498 for a := 0 to High(gAreas) do
6499 if gAreas[a].AreaType <> AREA_NONE then
6500 SelectObject(OBJECT_AREA, a, True);
6501 OBJECT_TRIGGER:
6502 if gTriggers <> nil then
6503 for a := 0 to High(gTriggers) do
6504 if gTriggers[a].TriggerType <> TRIGGER_NONE then
6505 SelectObject(OBJECT_TRIGGER, a, True);
6506 end;
6508 RecountSelectedObjects();
6509 end;
6511 procedure TMainForm.tbGridOnClick(Sender: TObject);
6512 begin
6513 DotEnable := not DotEnable;
6514 (Sender as TToolButton).Down := DotEnable;
6515 end;
6517 procedure TMainForm.OnIdle(Sender: TObject; var Done: Boolean);
6518 begin
6519 // FIXME: this is a shitty hack
6520 if not gDataLoaded then
6521 begin
6522 e_WriteLog('Init OpenGL', MSG_NOTIFY);
6523 e_InitGL();
6524 e_WriteLog('Loading data', MSG_NOTIFY);
6525 LoadStdFont('STDTXT', 'STDFONT', gEditorFont);
6526 e_WriteLog('Loading more data', MSG_NOTIFY);
6527 LoadData();
6528 e_WriteLog('Loading even more data', MSG_NOTIFY);
6529 gDataLoaded := True;
6530 MainForm.FormResize(nil);
6531 end;
6532 Draw();
6533 end;
6535 procedure TMainForm.miMapPreviewClick(Sender: TObject);
6536 begin
6537 if PreviewMode = 2 then
6538 Exit;
6540 if PreviewMode = 0 then
6541 begin
6542 Splitter2.Visible := False;
6543 Splitter1.Visible := False;
6544 StatusBar.Visible := False;
6545 PanelObjs.Visible := False;
6546 PanelProps.Visible := False;
6547 MainToolBar.Visible := False;
6548 sbHorizontal.Visible := False;
6549 sbVertical.Visible := False;
6550 end
6551 else
6552 begin
6553 StatusBar.Visible := True;
6554 PanelObjs.Visible := True;
6555 PanelProps.Visible := True;
6556 Splitter2.Visible := True;
6557 Splitter1.Visible := True;
6558 MainToolBar.Visible := True;
6559 sbHorizontal.Visible := True;
6560 sbVertical.Visible := True;
6561 end;
6563 PreviewMode := PreviewMode xor 1;
6564 (Sender as TMenuItem).Checked := PreviewMode > 0;
6566 FormResize(Self);
6567 end;
6569 procedure TMainForm.miLayer1Click(Sender: TObject);
6570 begin
6571 SwitchLayer(LAYER_BACK);
6572 end;
6574 procedure TMainForm.miLayer2Click(Sender: TObject);
6575 begin
6576 SwitchLayer(LAYER_WALLS);
6577 end;
6579 procedure TMainForm.miLayer3Click(Sender: TObject);
6580 begin
6581 SwitchLayer(LAYER_FOREGROUND);
6582 end;
6584 procedure TMainForm.miLayer4Click(Sender: TObject);
6585 begin
6586 SwitchLayer(LAYER_STEPS);
6587 end;
6589 procedure TMainForm.miLayer5Click(Sender: TObject);
6590 begin
6591 SwitchLayer(LAYER_WATER);
6592 end;
6594 procedure TMainForm.miLayer6Click(Sender: TObject);
6595 begin
6596 SwitchLayer(LAYER_ITEMS);
6597 end;
6599 procedure TMainForm.miLayer7Click(Sender: TObject);
6600 begin
6601 SwitchLayer(LAYER_MONSTERS);
6602 end;
6604 procedure TMainForm.miLayer8Click(Sender: TObject);
6605 begin
6606 SwitchLayer(LAYER_AREAS);
6607 end;
6609 procedure TMainForm.miLayer9Click(Sender: TObject);
6610 begin
6611 SwitchLayer(LAYER_TRIGGERS);
6612 end;
6614 procedure TMainForm.tbShowClick(Sender: TObject);
6615 var
6616 a: Integer;
6617 b: Boolean;
6618 begin
6619 b := True;
6620 for a := 0 to High(LayerEnabled) do
6621 b := b and LayerEnabled[a];
6623 b := not b;
6625 ShowLayer(LAYER_BACK, b);
6626 ShowLayer(LAYER_WALLS, b);
6627 ShowLayer(LAYER_FOREGROUND, b);
6628 ShowLayer(LAYER_STEPS, b);
6629 ShowLayer(LAYER_WATER, b);
6630 ShowLayer(LAYER_ITEMS, b);
6631 ShowLayer(LAYER_MONSTERS, b);
6632 ShowLayer(LAYER_AREAS, b);
6633 ShowLayer(LAYER_TRIGGERS, b);
6634 end;
6636 procedure TMainForm.miMiniMapClick(Sender: TObject);
6637 begin
6638 SwitchMap();
6639 end;
6641 procedure TMainForm.miSwitchGridClick(Sender: TObject);
6642 begin
6643 if DotStep = DotStepOne then
6644 DotStep := DotStepTwo
6645 else
6646 DotStep := DotStepOne;
6648 MousePos.X := (MousePos.X div DotStep) * DotStep;
6649 MousePos.Y := (MousePos.Y div DotStep) * DotStep;
6650 end;
6652 procedure TMainForm.miShowEdgesClick(Sender: TObject);
6653 begin
6654 ShowEdges();
6655 end;
6657 procedure TMainForm.miSnapToGridClick(Sender: TObject);
6658 begin
6659 SnapToGrid := not SnapToGrid;
6661 MousePos.X := (MousePos.X div DotStep) * DotStep;
6662 MousePos.Y := (MousePos.Y div DotStep) * DotStep;
6664 miSnapToGrid.Checked := SnapToGrid;
6665 end;
6667 procedure TMainForm.minexttabClick(Sender: TObject);
6668 begin
6669 if pcObjects.ActivePageIndex < pcObjects.PageCount-1 then
6670 pcObjects.ActivePageIndex := pcObjects.ActivePageIndex+1
6671 else
6672 pcObjects.ActivePageIndex := 0;
6673 end;
6675 procedure TMainForm.miSaveMiniMapClick(Sender: TObject);
6676 begin
6677 SaveMiniMapForm.ShowModal();
6678 end;
6680 procedure TMainForm.bClearTextureClick(Sender: TObject);
6681 begin
6682 lbTextureList.ItemIndex := -1;
6683 lTextureWidth.Caption := '';
6684 lTextureHeight.Caption := '';
6685 end;
6687 procedure TMainForm.miPackMapClick(Sender: TObject);
6688 begin
6689 PackMapForm.ShowModal();
6690 end;
6692 procedure TMainForm.miMapTestSettingsClick(Sender: TObject);
6693 begin
6694 MapTestForm.ShowModal();
6695 end;
6697 type SSArray = array of String;
6699 function ParseString (Str: AnsiString): SSArray;
6700 function GetStr (var Str: AnsiString): AnsiString;
6701 var a, b: Integer;
6702 begin
6703 Result := '';
6704 if Str[1] = '"' then
6705 for b := 1 to Length(Str) do
6706 if (b = Length(Str)) or (Str[b + 1] = '"') then
6707 begin
6708 Result := Copy(Str, 2, b - 1);
6709 Delete(Str, 1, b + 1);
6710 Str := Trim(Str);
6711 Exit;
6712 end;
6713 for a := 1 to Length(Str) do
6714 if (a = Length(Str)) or (Str[a + 1] = ' ') then
6715 begin
6716 Result := Copy(Str, 1, a);
6717 Delete(Str, 1, a + 1);
6718 Str := Trim(Str);
6719 Exit;
6720 end;
6721 end;
6722 begin
6723 Result := nil;
6724 Str := Trim(Str);
6725 while Str <> '' do
6726 begin
6727 SetLength(Result, Length(Result)+1);
6728 Result[High(Result)] := GetStr(Str);
6729 end;
6730 end;
6732 procedure TMainForm.miTestMapClick(Sender: TObject);
6733 var
6734 newWAD, oldWAD, tempMap, ext: String;
6735 args: SSArray;
6736 opt: LongWord;
6737 time, i: Integer;
6738 proc: TProcessUTF8;
6739 res: Boolean;
6740 begin
6741 // Сохраняем временную карту:
6742 time := 0;
6743 repeat
6744 newWAD := ExtractFilePath(TestD2dExe) + Format('maps/temp%.4d', [time]);
6745 Inc(time);
6746 until not FileExists(newWAD);
6747 if OpenedMap <> '' then
6748 begin
6749 oldWad := g_ExtractWadName(OpenedMap);
6750 newWad := newWad + ExtractFileExt(oldWad);
6751 if CopyFile(oldWad, newWad) = false then
6752 e_WriteLog('MapTest: unable to copy [' + oldWad + '] to [' + newWad + ']', MSG_WARNING)
6753 end
6754 else
6755 begin
6756 newWad := newWad + '.wad'
6757 end;
6758 tempMap := newWAD + ':\' + TEST_MAP_NAME;
6759 SaveMap(tempMap);
6760 tempMap := ExtractRelativePath(ExtractFilePath(TestD2dExe) + 'maps/', tempMap);
6762 // Опции игры:
6763 opt := 32 + 64;
6764 if TestOptionsTwoPlayers then
6765 opt := opt + 1;
6766 if TestOptionsTeamDamage then
6767 opt := opt + 2;
6768 if TestOptionsAllowExit then
6769 opt := opt + 4;
6770 if TestOptionsWeaponStay then
6771 opt := opt + 8;
6772 if TestOptionsMonstersDM then
6773 opt := opt + 16;
6775 // Запускаем:
6776 proc := TProcessUTF8.Create(nil);
6777 proc.Executable := TestD2dExe;
6778 proc.Parameters.Add('-map');
6779 proc.Parameters.Add(tempMap);
6780 proc.Parameters.Add('-gm');
6781 proc.Parameters.Add(TestGameMode);
6782 proc.Parameters.Add('-limt');
6783 proc.Parameters.Add(TestLimTime);
6784 proc.Parameters.Add('-lims');
6785 proc.Parameters.Add(TestLimScore);
6786 proc.Parameters.Add('-opt');
6787 proc.Parameters.Add(IntToStr(opt));
6788 proc.Parameters.Add('--debug');
6789 if TestMapOnce then
6790 proc.Parameters.Add('--close');
6792 args := ParseString(TestD2DArgs);
6793 for i := 0 to High(args) do
6794 proc.Parameters.Add(args[i]);
6796 res := True;
6797 try
6798 proc.Execute();
6799 except
6800 res := False;
6801 end;
6802 if res then
6803 begin
6804 Application.Minimize();
6805 proc.WaitOnExit();
6806 end;
6807 if (not res) or (proc.ExitCode < 0) then
6808 begin
6809 MessageBox(0, 'FIXME',
6810 PChar(_lc[I_MSG_EXEC_ERROR]),
6811 MB_OK or MB_ICONERROR);
6812 end;
6813 proc.Free();
6815 SysUtils.DeleteFile(newWAD);
6816 Application.Restore();
6817 end;
6819 procedure TMainForm.sbVerticalScroll(Sender: TObject;
6820 ScrollCode: TScrollCode; var ScrollPos: Integer);
6821 begin
6822 MapOffset.Y := -sbVertical.Position;
6823 end;
6825 procedure TMainForm.sbHorizontalScroll(Sender: TObject;
6826 ScrollCode: TScrollCode; var ScrollPos: Integer);
6827 begin
6828 MapOffset.X := -sbHorizontal.Position;
6829 end;
6831 procedure TMainForm.miOpenWadMapClick(Sender: TObject);
6832 begin
6833 if OpenedWAD <> '' then
6834 begin
6835 OpenMap(OpenedWAD, '');
6836 end;
6837 end;
6839 procedure TMainForm.selectall1Click(Sender: TObject);
6840 var
6841 a: Integer;
6842 begin
6843 RemoveSelectFromObjects();
6845 if gPanels <> nil then
6846 for a := 0 to High(gPanels) do
6847 if gPanels[a].PanelType <> PANEL_NONE then
6848 SelectObject(OBJECT_PANEL, a, True);
6850 if gItems <> nil then
6851 for a := 0 to High(gItems) do
6852 if gItems[a].ItemType <> ITEM_NONE then
6853 SelectObject(OBJECT_ITEM, a, True);
6855 if gMonsters <> nil then
6856 for a := 0 to High(gMonsters) do
6857 if gMonsters[a].MonsterType <> MONSTER_NONE then
6858 SelectObject(OBJECT_MONSTER, a, True);
6860 if gAreas <> nil then
6861 for a := 0 to High(gAreas) do
6862 if gAreas[a].AreaType <> AREA_NONE then
6863 SelectObject(OBJECT_AREA, a, True);
6865 if gTriggers <> nil then
6866 for a := 0 to High(gTriggers) do
6867 if gTriggers[a].TriggerType <> TRIGGER_NONE then
6868 SelectObject(OBJECT_TRIGGER, a, True);
6870 RecountSelectedObjects();
6871 end;
6873 procedure TMainForm.Splitter1CanResize(Sender: TObject;
6874 var NewSize: Integer; var Accept: Boolean);
6875 begin
6876 Accept := (NewSize > 140);
6877 end;
6879 procedure TMainForm.Splitter2CanResize(Sender: TObject;
6880 var NewSize: Integer; var Accept: Boolean);
6881 begin
6882 Accept := (NewSize > 110);
6883 end;
6885 procedure TMainForm.vleObjectPropertyEnter(Sender: TObject);
6886 begin
6887 EditingProperties := True;
6888 end;
6890 procedure TMainForm.vleObjectPropertyExit(Sender: TObject);
6891 begin
6892 EditingProperties := False;
6893 end;
6895 procedure TMainForm.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
6896 begin
6897 // Объекты передвигались:
6898 if MainForm.ActiveControl = RenderPanel then
6899 begin
6900 if (Key = VK_NUMPAD4) or
6901 (Key = VK_NUMPAD6) or
6902 (Key = VK_NUMPAD8) or
6903 (Key = VK_NUMPAD5) or
6904 (Key = Ord('V')) then
6905 FillProperty();
6906 end;
6907 // Быстрое превью карты:
6908 if Key = Ord('E') then
6909 begin
6910 if PreviewMode = 2 then
6911 PreviewMode := 0;
6912 end;
6913 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
6914 end;
6916 end.