DEADSOFTWARE

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