DEADSOFTWARE

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