DEADSOFTWARE

591b7e060f9405b89d54aadc8fc1791189bb3ca1
[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 MapTestTimer: TTimer;
19 Splitter1: TSplitter;
20 Splitter2: TSplitter;
21 StatusBar: TStatusBar;
22 OpenDialog: TOpenDialog;
23 SaveDialog: TSaveDialog;
24 ColorDialog: TColorDialog;
26 // Menu:
27 MainMenu: TMainMenu;
28 ImageList: TImageList;
29 // Apple menu:
30 miApple: TMenuItem;
31 miAppleAbout: TMenuItem;
32 miAppleLine0: TMenuItem;
33 miApplePref: TMenuItem;
34 miAppleLine1: TMenuItem;
35 // File menu:
36 miMenuFile: TMenuItem;
37 miNewMap: TMenuItem;
38 miOpenMap: TMenuItem;
39 miMacRecentSubMenu: TMenuItem;
40 miMacRecentEnd: TMenuItem;
41 miMacRecentClear: TMenuItem;
42 Separator1: TMenuItem;
43 miSaveMap: TMenuItem;
44 miSaveMapAs: TMenuItem;
45 miOpenWadMap: TMenuItem;
46 miLine1: TMenuItem;
47 miReopenMap: TMenuItem;
48 miSaveMiniMap: TMenuItem;
49 miDeleteMap: TMenuItem;
50 miPackMap: TMenuItem;
51 miWinRecentStart: TMenuItem;
52 miWinRecent: TMenuItem;
53 miLine2: TMenuItem;
54 miExit: TMenuItem;
55 // Edit menu:
56 miMenuEdit: TMenuItem;
57 miUndo: TMenuItem;
58 miLine3: TMenuItem;
59 miCopy: TMenuItem;
60 miCut: TMenuItem;
61 miPaste: TMenuItem;
62 miLine4: TMenuItem;
63 miSelectAll: TMenuItem;
64 miLine5: TMenuItem;
65 miSnapToGrid: TMenuItem;
66 miSwitchGrid: TMenuItem;
67 Separator2: TMenuItem;
68 miToFore: TMenuItem;
69 miToBack: TMenuItem;
70 miLine6: TMenuItem;
71 miMapOptions: TMenuItem;
72 miOptions: TMenuItem;
73 // View menu:
74 miMenuView: TMenuItem;
75 miLayers: TMenuItem;
76 miLayer1: TMenuItem;
77 miLayer2: TMenuItem;
78 miLayer3: TMenuItem;
79 miLayer4: TMenuItem;
80 miLayer5: TMenuItem;
81 miLayer6: TMenuItem;
82 miLayer7: TMenuItem;
83 miLayer8: TMenuItem;
84 miLayer9: TMenuItem;
85 miViewLine1: TMenuItem;
86 miMiniMap: TMenuItem;
87 miShowEdges: TMenuItem;
88 miViewLine2: TMenuItem;
89 miMapPreview: TMenuItem;
90 // Service menu:
91 miMenuService: TMenuItem;
92 miCheckMap: TMenuItem;
93 miOptimmization: TMenuItem;
94 miTestMap: TMenuItem;
95 // Window menu:
96 miMenuWindow: TMenuItem;
97 miMacMinimize: TMenuItem;
98 miMacZoom: TMenuItem;
99 // Help Menu:
100 miMenuHelp: TMenuItem;
101 miAbout: TMenuItem;
102 // HIDDEN menu:
103 miMenuHidden: TMenuItem;
104 minexttab: TMenuItem;
105 selectall1: TMenuItem;
107 // Toolbar:
108 ilToolbar: TImageList;
109 MainToolBar: TToolBar;
110 tbNewMap: TToolButton;
111 tbOpenMap: TToolButton;
112 tbSaveMap: TToolButton;
113 tbOpenWadMap: TToolButton;
114 tbLine1: TToolButton;
115 tbShowMap: TToolButton;
116 tbLine2: TToolButton;
117 tbShow: TToolButton;
118 pmShow: TPopupMenu;
119 miLayerP1: TMenuItem;
120 miLayerP2: TMenuItem;
121 miLayerP3: TMenuItem;
122 miLayerP4: TMenuItem;
123 miLayerP5: TMenuItem;
124 miLayerP6: TMenuItem;
125 miLayerP7: TMenuItem;
126 miLayerP8: TMenuItem;
127 miLayerP9: TMenuItem;
128 tbLine3: TToolButton;
129 tbGridOn: TToolButton;
130 tbGrid: TToolButton;
131 tbLine4: TToolButton;
132 tbTestMap: TToolButton;
134 // Progress bar:
135 pLoadProgress: TPanel;
136 lLoad: TLabel;
137 pbLoad: TProgressBar;
139 // Map edit area:
140 PanelMap: TPanel;
141 RenderPanel: TOpenGLControl;
142 sbHorizontal: TScrollBar;
143 sbVertical: TScrollBar;
145 // Object propertiy editor:
146 PanelProps: TPanel;
147 PanelPropApply: TPanel;
148 bApplyProperty: TButton;
149 vleObjectProperty: TValueListEditor;
151 // Object palette:
152 PanelObjs: TPanel;
153 pcObjects: TPageControl;
154 // Panels Tab:
155 tsPanels: TTabSheet;
156 PanelPanelType: TPanel;
157 lbPanelType: TListBox;
158 lbTextureList: TListBox;
159 PanelTextures: TPanel;
160 LabelTxW: TLabel;
161 lTextureWidth: TLabel;
162 LabelTxH: TLabel;
163 lTextureHeight: TLabel;
164 cbPreview: TCheckBox;
165 bbAddTexture: TBitBtn;
166 bbRemoveTexture: TBitBtn;
167 bClearTexture: TButton;
168 // Items Tab:
169 tsItems: TTabSheet;
170 lbItemList: TListBox;
171 cbOnlyDM: TCheckBox;
172 cbFall: TCheckBox;
173 // Monsters Tab:
174 tsMonsters: TTabSheet;
175 lbMonsterList: TListBox;
176 rbMonsterLeft: TRadioButton;
177 rbMonsterRight: TRadioButton;
178 // Areas Tab:
179 tsAreas: TTabSheet;
180 lbAreasList: TListBox;
181 rbAreaLeft: TRadioButton;
182 rbAreaRight: TRadioButton;
183 // Triggers Tab:
184 tsTriggers: TTabSheet;
185 lbTriggersList: TListBox;
186 clbActivationType: TCheckListBox;
187 clbKeys: TCheckListBox;
189 procedure aAboutExecute(Sender: TObject);
190 procedure aCheckMapExecute(Sender: TObject);
191 procedure aMoveToFore(Sender: TObject);
192 procedure aMoveToBack(Sender: TObject);
193 procedure aCopyObjectExecute(Sender: TObject);
194 procedure aCutObjectExecute(Sender: TObject);
195 procedure aEditorOptionsExecute(Sender: TObject);
196 procedure aExitExecute(Sender: TObject);
197 procedure aMapOptionsExecute(Sender: TObject);
198 procedure aNewMapExecute(Sender: TObject);
199 procedure aOpenMapExecute(Sender: TObject);
200 procedure aOptimizeExecute(Sender: TObject);
201 procedure aPasteObjectExecute(Sender: TObject);
202 procedure aSelectAllExecute(Sender: TObject);
203 procedure aSaveMapExecute(Sender: TObject);
204 procedure aSaveMapAsExecute(Sender: TObject);
205 procedure aUndoExecute(Sender: TObject);
206 procedure aDeleteMap(Sender: TObject);
207 procedure bApplyPropertyClick(Sender: TObject);
208 procedure bbAddTextureClick(Sender: TObject);
209 procedure bbRemoveTextureClick(Sender: TObject);
210 procedure FormActivate(Sender: TObject);
211 procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
212 procedure FormCreate(Sender: TObject);
213 procedure FormDestroy(Sender: TObject);
214 procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
215 procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
216 procedure FormResize(Sender: TObject);
217 procedure FormWindowStateChange(Sender: TObject);
218 procedure miMacRecentClearClick(Sender: TObject);
219 procedure miMacZoomClick(Sender: TObject);
220 procedure lbTextureListClick(Sender: TObject);
221 procedure lbTextureListDrawItem(Control: TWinControl; Index: Integer;
222 ARect: TRect; State: TOwnerDrawState);
223 procedure miMacMinimizeClick(Sender: TObject);
224 procedure miReopenMapClick(Sender: TObject);
225 procedure RenderPanelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
226 procedure RenderPanelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
227 procedure RenderPanelMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
228 procedure RenderPanelPaint(Sender: TObject);
229 procedure RenderPanelResize(Sender: TObject);
230 procedure Splitter1Moved(Sender: TObject);
231 procedure MapTestCheck(Sender: TObject);
232 procedure vleObjectPropertyEditButtonClick(Sender: TObject);
233 procedure vleObjectPropertyApply(Sender: TObject);
234 procedure vleObjectPropertyGetPickList(Sender: TObject; const KeyName: String; Values: TStrings);
235 procedure vleObjectPropertyKeyDown(Sender: TObject; var Key: Word;
236 Shift: TShiftState);
237 procedure tbGridOnClick(Sender: TObject);
238 procedure miMapPreviewClick(Sender: TObject);
239 procedure miLayer1Click(Sender: TObject);
240 procedure miLayer2Click(Sender: TObject);
241 procedure miLayer3Click(Sender: TObject);
242 procedure miLayer4Click(Sender: TObject);
243 procedure miLayer5Click(Sender: TObject);
244 procedure miLayer6Click(Sender: TObject);
245 procedure miLayer7Click(Sender: TObject);
246 procedure miLayer8Click(Sender: TObject);
247 procedure miLayer9Click(Sender: TObject);
248 procedure tbShowClick(Sender: TObject);
249 procedure miSnapToGridClick(Sender: TObject);
250 procedure miMiniMapClick(Sender: TObject);
251 procedure miSwitchGridClick(Sender: TObject);
252 procedure miShowEdgesClick(Sender: TObject);
253 procedure minexttabClick(Sender: TObject);
254 procedure miSaveMiniMapClick(Sender: TObject);
255 procedure bClearTextureClick(Sender: TObject);
256 procedure miPackMapClick(Sender: TObject);
257 procedure miTestMapClick(Sender: TObject);
258 procedure sbVerticalScroll(Sender: TObject; ScrollCode: TScrollCode;
259 var ScrollPos: Integer);
260 procedure sbHorizontalScroll(Sender: TObject; ScrollCode: TScrollCode;
261 var ScrollPos: Integer);
262 procedure miOpenWadMapClick(Sender: TObject);
263 procedure selectall1Click(Sender: TObject);
264 procedure Splitter1CanResize(Sender: TObject; var NewSize: Integer;
265 var Accept: Boolean);
266 procedure Splitter2CanResize(Sender: TObject; var NewSize: Integer;
267 var Accept: Boolean);
268 procedure vleObjectPropertyEnter(Sender: TObject);
269 procedure vleObjectPropertyExit(Sender: TObject);
270 procedure FormKeyUp(Sender: TObject; var Key: Word;
271 Shift: TShiftState);
272 private
273 procedure Draw();
274 procedure OnIdle(Sender: TObject; var Done: Boolean);
275 procedure RefillRecentMenu (menu: TMenuItem; start: Integer; fmt: AnsiString);
276 public
277 procedure RefreshRecentMenu();
278 procedure OpenMapFile(FileName: String);
279 function RenderMousePos(): TPoint;
280 procedure RecountSelectedObjects();
281 end;
283 const
284 LAYER_BACK = 0;
285 LAYER_WALLS = 1;
286 LAYER_FOREGROUND = 2;
287 LAYER_STEPS = 3;
288 LAYER_WATER = 4;
289 LAYER_ITEMS = 5;
290 LAYER_MONSTERS = 6;
291 LAYER_AREAS = 7;
292 LAYER_TRIGGERS = 8;
294 TEST_MAP_NAME = '$$$_TEST_$$$';
295 LANGUAGE_FILE_NAME = '_Editor.txt';
297 var
298 MainForm: TMainForm;
299 StartMap: String;
300 OpenedMap: String;
301 OpenedWAD: String;
303 DotColor: TColor;
304 DotEnable: Boolean;
305 DotStep: Word;
306 DotStepOne, DotStepTwo: Word;
307 DotSize: Byte;
308 DrawTexturePanel: Boolean;
309 DrawPanelSize: Boolean;
310 BackColor: TColor;
311 PreviewColor: TColor;
312 UseCheckerboard: Boolean;
313 Scale: Byte;
314 RecentCount: Integer;
315 RecentFiles: TStringList;
316 slInvalidTextures: TStringList;
318 TestGameMode: String;
319 TestLimTime: String;
320 TestLimScore: String;
321 TestOptionsTwoPlayers: Boolean;
322 TestOptionsTeamDamage: Boolean;
323 TestOptionsAllowExit: Boolean;
324 TestOptionsWeaponStay: Boolean;
325 TestOptionsMonstersDM: Boolean;
326 TestD2dExe, TestD2DArgs: String;
327 TestMapOnce: Boolean;
329 LayerEnabled: Array [LAYER_BACK..LAYER_TRIGGERS] of Boolean =
330 (True, True, True, True, True, True, True, True, True);
331 ContourEnabled: Array [LAYER_BACK..LAYER_TRIGGERS] of Boolean =
332 (False, False, False, False, False, False, False, False, False);
333 PreviewMode: Byte = 0;
334 gLanguage: String;
336 FormCaption: String;
339 procedure OpenMap(FileName: String; mapN: String);
340 function AddTexture(aWAD, aSection, aTex: String; silent: Boolean): Boolean;
341 procedure RemoveSelectFromObjects();
342 procedure ChangeShownProperty(Name: String; NewValue: String);
344 implementation
346 uses
347 f_options, e_graphics, e_log, GL, Math,
348 f_mapoptions, g_basic, f_about, f_mapoptimization,
349 f_mapcheck, f_addresource_texture, g_textures,
350 f_activationtype, f_keys, wadreader, fileutil,
351 MAPREADER, f_selectmap, f_savemap, WADEDITOR, MAPDEF,
352 g_map, f_saveminimap, f_addresource, CONFIG, f_packmap,
353 f_addresource_sound, f_choosetype,
354 g_language, ClipBrd, g_resources, g_options;
356 const
357 UNDO_DELETE_PANEL = 1;
358 UNDO_DELETE_ITEM = 2;
359 UNDO_DELETE_AREA = 3;
360 UNDO_DELETE_MONSTER = 4;
361 UNDO_DELETE_TRIGGER = 5;
362 UNDO_ADD_PANEL = 6;
363 UNDO_ADD_ITEM = 7;
364 UNDO_ADD_AREA = 8;
365 UNDO_ADD_MONSTER = 9;
366 UNDO_ADD_TRIGGER = 10;
367 UNDO_MOVE_PANEL = 11;
368 UNDO_MOVE_ITEM = 12;
369 UNDO_MOVE_AREA = 13;
370 UNDO_MOVE_MONSTER = 14;
371 UNDO_MOVE_TRIGGER = 15;
372 UNDO_RESIZE_PANEL = 16;
373 UNDO_RESIZE_TRIGGER = 17;
375 MOUSEACTION_NONE = 0;
376 MOUSEACTION_DRAWPANEL = 1;
377 MOUSEACTION_DRAWTRIGGER = 2;
378 MOUSEACTION_MOVEOBJ = 3;
379 MOUSEACTION_RESIZE = 4;
380 MOUSEACTION_MOVEMAP = 5;
381 MOUSEACTION_DRAWPRESS = 6;
382 MOUSEACTION_NOACTION = 7;
384 RESIZETYPE_NONE = 0;
385 RESIZETYPE_VERTICAL = 1;
386 RESIZETYPE_HORIZONTAL = 2;
388 RESIZEDIR_NONE = 0;
389 RESIZEDIR_DOWN = 1;
390 RESIZEDIR_UP = 2;
391 RESIZEDIR_RIGHT = 3;
392 RESIZEDIR_LEFT = 4;
394 SELECTFLAG_NONE = 0;
395 SELECTFLAG_TELEPORT = 1;
396 SELECTFLAG_DOOR = 2;
397 SELECTFLAG_TEXTURE = 3;
398 SELECTFLAG_LIFT = 4;
399 SELECTFLAG_MONSTER = 5;
400 SELECTFLAG_SPAWNPOINT = 6;
401 SELECTFLAG_SHOTPANEL = 7;
402 SELECTFLAG_SELECTED = 8;
404 RECENT_FILES_MENU_START = 12;
406 CLIPBOARD_SIG = 'DF:ED';
408 type
409 TUndoRec = record
410 UndoType: Byte;
411 case Byte of
412 UNDO_DELETE_PANEL: (Panel: ^TPanel);
413 UNDO_DELETE_ITEM: (Item: TItem);
414 UNDO_DELETE_AREA: (Area: TArea);
415 UNDO_DELETE_MONSTER: (Monster: TMonster);
416 UNDO_DELETE_TRIGGER: (Trigger: TTrigger);
417 UNDO_ADD_PANEL,
418 UNDO_ADD_ITEM,
419 UNDO_ADD_AREA,
420 UNDO_ADD_MONSTER,
421 UNDO_ADD_TRIGGER: (AddID: DWORD);
422 UNDO_MOVE_PANEL,
423 UNDO_MOVE_ITEM,
424 UNDO_MOVE_AREA,
425 UNDO_MOVE_MONSTER,
426 UNDO_MOVE_TRIGGER: (MoveID: DWORD; dX, dY: Integer);
427 UNDO_RESIZE_PANEL,
428 UNDO_RESIZE_TRIGGER: (ResizeID: DWORD; dW, dH: Integer);
429 end;
431 TCopyRec = record
432 ObjectType: Byte;
433 ID: Cardinal;
434 case Byte of
435 OBJECT_PANEL: (Panel: ^TPanel);
436 OBJECT_ITEM: (Item: TItem);
437 OBJECT_AREA: (Area: TArea);
438 OBJECT_MONSTER: (Monster: TMonster);
439 OBJECT_TRIGGER: (Trigger: TTrigger);
440 end;
442 TCopyRecArray = Array of TCopyRec;
444 var
445 gEditorFont: DWORD;
446 gDataLoaded: Boolean = False;
447 ShowMap: Boolean = False;
448 DrawRect: PRect = nil;
449 SnapToGrid: Boolean = True;
451 MousePos: Types.TPoint;
452 LastMovePoint: Types.TPoint;
453 MouseLDown: Boolean;
454 MouseRDown: Boolean;
455 MouseMDown: Boolean;
456 MouseLDownPos: Types.TPoint;
457 MouseRDownPos: Types.TPoint;
458 MouseMDownPos: Types.TPoint;
460 SelectFlag: Byte = SELECTFLAG_NONE;
461 MouseAction: Byte = MOUSEACTION_NONE;
462 ResizeType: Byte = RESIZETYPE_NONE;
463 ResizeDirection: Byte = RESIZEDIR_NONE;
465 DrawPressRect: Boolean = False;
466 EditingProperties: Boolean = False;
468 UndoBuffer: Array of Array of TUndoRec = nil;
470 MapTestProcess: TProcessUTF8;
471 MapTestFile: String;
473 {$R *.lfm}
475 //----------------------------------------
476 //Далее идут вспомогательные процедуры
477 //----------------------------------------
479 function NameToBool(Name: String): Boolean;
480 begin
481 if Name = BoolNames[True] then
482 Result := True
483 else
484 Result := False;
485 end;
487 function NameToDir(Name: String): TDirection;
488 begin
489 if Name = DirNames[D_LEFT] then
490 Result := D_LEFT
491 else
492 Result := D_RIGHT;
493 end;
495 function NameToDirAdv(Name: String): Byte;
496 begin
497 if Name = DirNamesAdv[1] then
498 Result := 1
499 else
500 if Name = DirNamesAdv[2] then
501 Result := 2
502 else
503 if Name = DirNamesAdv[3] then
504 Result := 3
505 else
506 Result := 0;
507 end;
509 function ActivateToStr(ActivateType: Byte): String;
510 begin
511 Result := '';
513 if ByteBool(ACTIVATE_PLAYERCOLLIDE and ActivateType) then
514 Result := Result + '+PC';
515 if ByteBool(ACTIVATE_MONSTERCOLLIDE and ActivateType) then
516 Result := Result + '+MC';
517 if ByteBool(ACTIVATE_PLAYERPRESS and ActivateType) then
518 Result := Result + '+PP';
519 if ByteBool(ACTIVATE_MONSTERPRESS and ActivateType) then
520 Result := Result + '+MP';
521 if ByteBool(ACTIVATE_SHOT and ActivateType) then
522 Result := Result + '+SH';
523 if ByteBool(ACTIVATE_NOMONSTER and ActivateType) then
524 Result := Result + '+NM';
526 if (Result <> '') and (Result[1] = '+') then
527 Delete(Result, 1, 1);
528 end;
530 function StrToActivate(Str: String): Byte;
531 begin
532 Result := 0;
534 if Pos('PC', Str) > 0 then
535 Result := ACTIVATE_PLAYERCOLLIDE;
536 if Pos('MC', Str) > 0 then
537 Result := Result or ACTIVATE_MONSTERCOLLIDE;
538 if Pos('PP', Str) > 0 then
539 Result := Result or ACTIVATE_PLAYERPRESS;
540 if Pos('MP', Str) > 0 then
541 Result := Result or ACTIVATE_MONSTERPRESS;
542 if Pos('SH', Str) > 0 then
543 Result := Result or ACTIVATE_SHOT;
544 if Pos('NM', Str) > 0 then
545 Result := Result or ACTIVATE_NOMONSTER;
546 end;
548 function KeyToStr(Key: Byte): String;
549 begin
550 Result := '';
552 if ByteBool(KEY_RED and Key) then
553 Result := Result + '+RK';
554 if ByteBool(KEY_GREEN and Key) then
555 Result := Result + '+GK';
556 if ByteBool(KEY_BLUE and Key) then
557 Result := Result + '+BK';
558 if ByteBool(KEY_REDTEAM and Key) then
559 Result := Result + '+RT';
560 if ByteBool(KEY_BLUETEAM and Key) then
561 Result := Result + '+BT';
563 if (Result <> '') and (Result[1] = '+') then
564 Delete(Result, 1, 1);
565 end;
567 function StrToKey(Str: String): Byte;
568 begin
569 Result := 0;
571 if Pos('RK', Str) > 0 then
572 Result := KEY_RED;
573 if Pos('GK', Str) > 0 then
574 Result := Result or KEY_GREEN;
575 if Pos('BK', Str) > 0 then
576 Result := Result or KEY_BLUE;
577 if Pos('RT', Str) > 0 then
578 Result := Result or KEY_REDTEAM;
579 if Pos('BT', Str) > 0 then
580 Result := Result or KEY_BLUETEAM;
581 end;
583 function EffectToStr(Effect: Byte): String;
584 begin
585 if Effect in [EFFECT_TELEPORT..EFFECT_FIRE] then
586 Result := EffectNames[Effect]
587 else
588 Result := EffectNames[EFFECT_NONE];
589 end;
591 function StrToEffect(Str: String): Byte;
592 var
593 i: Integer;
594 begin
595 Result := EFFECT_NONE;
596 for i := EFFECT_TELEPORT to EFFECT_FIRE do
597 if EffectNames[i] = Str then
598 begin
599 Result := i;
600 Exit;
601 end;
602 end;
604 function MonsterToStr(MonType: Byte): String;
605 begin
606 if MonType in [MONSTER_DEMON..MONSTER_MAN] then
607 Result := MonsterNames[MonType]
608 else
609 Result := MonsterNames[MONSTER_ZOMBY];
610 end;
612 function StrToMonster(Str: String): Byte;
613 var
614 i: Integer;
615 begin
616 Result := MONSTER_ZOMBY;
617 for i := MONSTER_DEMON to MONSTER_MAN do
618 if MonsterNames[i] = Str then
619 begin
620 Result := i;
621 Exit;
622 end;
623 end;
625 function ItemToStr(ItemType: Byte): String;
626 begin
627 if ItemType in [ITEM_MEDKIT_SMALL..ITEM_MAX] then
628 Result := ItemNames[ItemType]
629 else
630 Result := ItemNames[ITEM_AMMO_BULLETS];
631 end;
633 function StrToItem(Str: String): Byte;
634 var
635 i: Integer;
636 begin
637 Result := ITEM_AMMO_BULLETS;
638 for i := ITEM_MEDKIT_SMALL to ITEM_MAX do
639 if ItemNames[i] = Str then
640 begin
641 Result := i;
642 Exit;
643 end;
644 end;
646 function ShotToStr(ShotType: Byte): String;
647 begin
648 if ShotType in [TRIGGER_SHOT_PISTOL..TRIGGER_SHOT_MAX] then
649 Result := ShotNames[ShotType]
650 else
651 Result := ShotNames[TRIGGER_SHOT_PISTOL];
652 end;
654 function StrToShot(Str: String): Byte;
655 var
656 i: Integer;
657 begin
658 Result := TRIGGER_SHOT_PISTOL;
659 for i := TRIGGER_SHOT_PISTOL to TRIGGER_SHOT_MAX do
660 if ShotNames[i] = Str then
661 begin
662 Result := i;
663 Exit;
664 end;
665 end;
667 function SelectedObjectCount(): Word;
668 var
669 a: Integer;
670 begin
671 Result := 0;
673 if SelectedObjects = nil then
674 Exit;
676 for a := 0 to High(SelectedObjects) do
677 if SelectedObjects[a].Live then
678 Result := Result + 1;
679 end;
681 function GetFirstSelected(): Integer;
682 var
683 a: Integer;
684 begin
685 Result := -1;
687 if SelectedObjects = nil then
688 Exit;
690 for a := 0 to High(SelectedObjects) do
691 if SelectedObjects[a].Live then
692 begin
693 Result := a;
694 Exit;
695 end;
696 end;
698 function Normalize16(x: Integer): Integer;
699 begin
700 Result := (x div 16) * 16;
701 end;
703 procedure MoveMap(X, Y: Integer);
704 var
705 rx, ry, ScaleSz: Integer;
706 begin
707 with MainForm.RenderPanel do
708 begin
709 ScaleSz := 16 div Scale;
710 // Размер видимой части карты:
711 rx := Min(Normalize16(Width), Normalize16(gMapInfo.Width)) div 2;
712 ry := Min(Normalize16(Height), Normalize16(gMapInfo.Height)) div 2;
713 // Место клика на мини-карте:
714 MapOffset.X := X - (Width - Max(gMapInfo.Width div ScaleSz, 1) - 1);
715 MapOffset.Y := Y - 1;
716 // Это же место на "большой" карте:
717 MapOffset.X := MapOffset.X * ScaleSz;
718 MapOffset.Y := MapOffset.Y * ScaleSz;
719 // Левый верхний угол новой видимой части карты:
720 MapOffset.X := MapOffset.X - rx;
721 MapOffset.Y := MapOffset.Y - ry;
722 // Выход за границы:
723 MapOffset.X := EnsureRange(MapOffset.X, MainForm.sbHorizontal.Min, MainForm.sbHorizontal.Max);
724 MapOffset.Y := EnsureRange(MapOffset.Y, MainForm.sbVertical.Min, MainForm.sbVertical.Max);
725 // Кратно 16:
726 // MapOffset.X := Normalize16(MapOffset.X);
727 // MapOffset.Y := Normalize16(MapOffset.Y);
728 end;
730 MainForm.sbHorizontal.Position := MapOffset.X;
731 MainForm.sbVertical.Position := MapOffset.Y;
733 MapOffset.X := -MapOffset.X;
734 MapOffset.Y := -MapOffset.Y;
736 MainForm.Resize();
737 end;
739 function IsTexturedPanel(PanelType: Word): Boolean;
740 begin
741 Result := WordBool(PanelType and (PANEL_WALL or PANEL_BACK or PANEL_FORE or
742 PANEL_STEP or PANEL_OPENDOOR or PANEL_CLOSEDOOR or
743 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2));
744 end;
746 procedure FillProperty();
747 var
748 _id: DWORD;
749 str: String;
750 begin
751 MainForm.vleObjectProperty.Strings.Clear();
752 MainForm.RecountSelectedObjects();
754 // Отображаем свойства если выделен только один объект:
755 if SelectedObjectCount() <> 1 then
756 Exit;
758 _id := GetFirstSelected();
759 if not SelectedObjects[_id].Live then
760 Exit;
762 with MainForm.vleObjectProperty do
763 with ItemProps[InsertRow(MsgPropId, IntToStr(SelectedObjects[_id].ID), True)] do
764 begin
765 EditStyle := esSimple;
766 ReadOnly := True;
767 end;
769 case SelectedObjects[0].ObjectType of
770 OBJECT_PANEL:
771 begin
772 with MainForm.vleObjectProperty,
773 gPanels[SelectedObjects[_id].ID] do
774 begin
775 with ItemProps[InsertRow(MsgPropX, IntToStr(X), True)] do
776 begin
777 EditStyle := esSimple;
778 MaxLength := 5;
779 end;
781 with ItemProps[InsertRow(MsgPropY, IntToStr(Y), True)] do
782 begin
783 EditStyle := esSimple;
784 MaxLength := 5;
785 end;
787 with ItemProps[InsertRow(MsgPropWidth, IntToStr(Width), True)] do
788 begin
789 EditStyle := esSimple;
790 MaxLength := 5;
791 end;
793 with ItemProps[InsertRow(MsgPropHeight, IntToStr(Height), True)] do
794 begin
795 EditStyle := esSimple;
796 MaxLength := 5;
797 end;
799 with ItemProps[InsertRow(MsgPropPanelType, GetPanelName(PanelType), True)] do
800 begin
801 EditStyle := esEllipsis;
802 ReadOnly := True;
803 end;
805 if IsTexturedPanel(PanelType) then
806 begin // Может быть текстура
807 with ItemProps[InsertRow(MsgPropPanelTex, TextureName, True)] do
808 begin
809 EditStyle := esEllipsis;
810 ReadOnly := True;
811 end;
813 if TextureName <> '' then
814 begin // Есть текстура
815 with ItemProps[InsertRow(MsgPropPanelAlpha, IntToStr(Alpha), True)] do
816 begin
817 EditStyle := esSimple;
818 MaxLength := 3;
819 end;
821 with ItemProps[InsertRow(MsgPropPanelBlend, BoolNames[Blending], True)] do
822 begin
823 EditStyle := esPickList;
824 ReadOnly := True;
825 end;
826 end;
827 end;
828 end;
829 end;
831 OBJECT_ITEM:
832 begin
833 with MainForm.vleObjectProperty,
834 gItems[SelectedObjects[_id].ID] do
835 begin
836 with ItemProps[InsertRow(MsgPropX, IntToStr(X), True)] do
837 begin
838 EditStyle := esSimple;
839 MaxLength := 5;
840 end;
842 with ItemProps[InsertRow(MsgPropY, IntToStr(Y), True)] do
843 begin
844 EditStyle := esSimple;
845 MaxLength := 5;
846 end;
848 with ItemProps[InsertRow(MsgPropDmOnly, BoolNames[OnlyDM], True)] do
849 begin
850 EditStyle := esPickList;
851 ReadOnly := True;
852 end;
854 with ItemProps[InsertRow(MsgPropItemFalls, BoolNames[Fall], True)] do
855 begin
856 EditStyle := esPickList;
857 ReadOnly := True;
858 end;
859 end;
860 end;
862 OBJECT_MONSTER:
863 begin
864 with MainForm.vleObjectProperty,
865 gMonsters[SelectedObjects[_id].ID] do
866 begin
867 with ItemProps[InsertRow(MsgPropX, IntToStr(X), True)] do
868 begin
869 EditStyle := esSimple;
870 MaxLength := 5;
871 end;
873 with ItemProps[InsertRow(MsgPropY, IntToStr(Y), True)] do
874 begin
875 EditStyle := esSimple;
876 MaxLength := 5;
877 end;
879 with ItemProps[InsertRow(MsgPropDirection, DirNames[Direction], True)] do
880 begin
881 EditStyle := esPickList;
882 ReadOnly := True;
883 end;
884 end;
885 end;
887 OBJECT_AREA:
888 begin
889 with MainForm.vleObjectProperty,
890 gAreas[SelectedObjects[_id].ID] do
891 begin
892 with ItemProps[InsertRow(MsgPropX, IntToStr(X), True)] do
893 begin
894 EditStyle := esSimple;
895 MaxLength := 5;
896 end;
898 with ItemProps[InsertRow(MsgPropY, IntToStr(Y), True)] do
899 begin
900 EditStyle := esSimple;
901 MaxLength := 5;
902 end;
904 with ItemProps[InsertRow(MsgPropDirection, DirNames[Direction], True)] do
905 begin
906 EditStyle := esPickList;
907 ReadOnly := True;
908 end;
909 end;
910 end;
912 OBJECT_TRIGGER:
913 begin
914 with MainForm.vleObjectProperty,
915 gTriggers[SelectedObjects[_id].ID] do
916 begin
917 with ItemProps[InsertRow(MsgPropTrType, GetTriggerName(TriggerType), True)] do
918 begin
919 EditStyle := esSimple;
920 ReadOnly := True;
921 end;
923 with ItemProps[InsertRow(MsgPropX, IntToStr(X), True)] do
924 begin
925 EditStyle := esSimple;
926 MaxLength := 5;
927 end;
929 with ItemProps[InsertRow(MsgPropY, IntToStr(Y), True)] do
930 begin
931 EditStyle := esSimple;
932 MaxLength := 5;
933 end;
935 with ItemProps[InsertRow(MsgPropWidth, IntToStr(Width), True)] do
936 begin
937 EditStyle := esSimple;
938 MaxLength := 5;
939 end;
941 with ItemProps[InsertRow(MsgPropHeight, IntToStr(Height), True)] do
942 begin
943 EditStyle := esSimple;
944 MaxLength := 5;
945 end;
947 with ItemProps[InsertRow(MsgPropTrEnabled, BoolNames[Enabled], True)] do
948 begin
949 EditStyle := esPickList;
950 ReadOnly := True;
951 end;
953 with ItemProps[InsertRow(MsgPropTrTexturePanel, IntToStr(TexturePanel), True)] do
954 begin
955 EditStyle := esEllipsis;
956 ReadOnly := True;
957 end;
959 with ItemProps[InsertRow(MsgPropTrActivation, ActivateToStr(ActivateType), True)] do
960 begin
961 EditStyle := esEllipsis;
962 ReadOnly := True;
963 end;
965 with ItemProps[InsertRow(MsgPropTrKeys, KeyToStr(Key), True)] do
966 begin
967 EditStyle := esEllipsis;
968 ReadOnly := True;
969 end;
971 case TriggerType of
972 TRIGGER_EXIT:
973 begin
974 str := win2utf(Data.MapName);
975 with ItemProps[InsertRow(MsgPropTrNextMap, str, True)] do
976 begin
977 EditStyle := esEllipsis;
978 ReadOnly := True;
979 end;
980 end;
982 TRIGGER_TELEPORT:
983 begin
984 with ItemProps[InsertRow(MsgPropTrTeleportTo, Format('(%d:%d)', [Data.TargetPoint.X, Data.TargetPoint.Y]), True)] do
985 begin
986 EditStyle := esEllipsis;
987 ReadOnly := True;
988 end;
990 with ItemProps[InsertRow(MsgPropTrD2d, BoolNames[Data.d2d_teleport], True)] do
991 begin
992 EditStyle := esPickList;
993 ReadOnly := True;
994 end;
996 with ItemProps[InsertRow(MsgPropTrTeleportSilent, BoolNames[Data.silent_teleport], True)] do
997 begin
998 EditStyle := esPickList;
999 ReadOnly := True;
1000 end;
1002 with ItemProps[InsertRow(MsgPropTrTeleportDir, DirNamesAdv[Data.TlpDir], True)] do
1003 begin
1004 EditStyle := esPickList;
1005 ReadOnly := True;
1006 end;
1007 end;
1009 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR,
1010 TRIGGER_DOOR, TRIGGER_DOOR5:
1011 begin
1012 with ItemProps[InsertRow(MsgPropTrDoorPanel, IntToStr(Data.PanelID), True)] do
1013 begin
1014 EditStyle := esEllipsis;
1015 ReadOnly := True;
1016 end;
1018 with ItemProps[InsertRow(MsgPropTrSilent, BoolNames[Data.NoSound], True)] do
1019 begin
1020 EditStyle := esPickList;
1021 ReadOnly := True;
1022 end;
1024 with ItemProps[InsertRow(MsgPropTrD2d, BoolNames[Data.d2d_doors], True)] do
1025 begin
1026 EditStyle := esPickList;
1027 ReadOnly := True;
1028 end;
1029 end;
1031 TRIGGER_CLOSETRAP, TRIGGER_TRAP:
1032 begin
1033 with ItemProps[InsertRow(MsgPropTrTrapPanel, IntToStr(Data.PanelID), True)] do
1034 begin
1035 EditStyle := esEllipsis;
1036 ReadOnly := True;
1037 end;
1039 with ItemProps[InsertRow(MsgPropTrSilent, BoolNames[Data.NoSound], True)] do
1040 begin
1041 EditStyle := esPickList;
1042 ReadOnly := True;
1043 end;
1045 with ItemProps[InsertRow(MsgPropTrD2d, BoolNames[Data.d2d_doors], True)] do
1046 begin
1047 EditStyle := esPickList;
1048 ReadOnly := True;
1049 end;
1050 end;
1052 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF,
1053 TRIGGER_ONOFF:
1054 begin
1055 with ItemProps[InsertRow(MsgPropTrExArea,
1056 Format('(%d:%d %d:%d)', [Data.tX, Data.tY, Data.tWidth, Data.tHeight]), True)] do
1057 begin
1058 EditStyle := esEllipsis;
1059 ReadOnly := True;
1060 end;
1062 with ItemProps[InsertRow(MsgPropTrExDelay, IntToStr(Data.Wait), True)] do
1063 begin
1064 EditStyle := esSimple;
1065 MaxLength := 5;
1066 end;
1068 with ItemProps[InsertRow(MsgPropTrExCount, IntToStr(Data.Count), True)] do
1069 begin
1070 EditStyle := esSimple;
1071 MaxLength := 5;
1072 end;
1074 with ItemProps[InsertRow(MsgPropTrExMonster, IntToStr(Data.MonsterID-1), True)] do
1075 begin
1076 EditStyle := esEllipsis;
1077 ReadOnly := True;
1078 end;
1080 if TriggerType = TRIGGER_PRESS then
1081 with ItemProps[InsertRow(MsgPropTrExRandom, BoolNames[Data.ExtRandom], True)] do
1082 begin
1083 EditStyle := esPickList;
1084 ReadOnly := True;
1085 end;
1086 end;
1088 TRIGGER_SECRET:
1091 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
1092 begin
1093 with ItemProps[InsertRow(MsgPropTrLiftPanel, IntToStr(Data.PanelID), True)] do
1094 begin
1095 EditStyle := esEllipsis;
1096 ReadOnly := True;
1097 end;
1099 with ItemProps[InsertRow(MsgPropTrSilent, BoolNames[Data.NoSound], True)] do
1100 begin
1101 EditStyle := esPickList;
1102 ReadOnly := True;
1103 end;
1105 with ItemProps[InsertRow(MsgPropTrD2d, BoolNames[Data.d2d_doors], True)] do
1106 begin
1107 EditStyle := esPickList;
1108 ReadOnly := True;
1109 end;
1110 end;
1112 TRIGGER_TEXTURE:
1113 begin
1114 with ItemProps[InsertRow(MsgPropTrTextureOnce, BoolNames[Data.ActivateOnce], True)] do
1115 begin
1116 EditStyle := esPickList;
1117 ReadOnly := True;
1118 end;
1120 with ItemProps[InsertRow(MsgPropTrTextureAnimOnce, BoolNames[Data.AnimOnce], True)] do
1121 begin
1122 EditStyle := esPickList;
1123 ReadOnly := True;
1124 end;
1125 end;
1127 TRIGGER_SOUND:
1128 begin
1129 str := win2utf(Data.SoundName);
1130 with ItemProps[InsertRow(MsgPropTrSoundName, str, True)] do
1131 begin
1132 EditStyle := esEllipsis;
1133 ReadOnly := True;
1134 end;
1136 with ItemProps[InsertRow(MsgPropTrSoundVolume, IntToStr(Data.Volume), True)] do
1137 begin
1138 EditStyle := esSimple;
1139 MaxLength := 3;
1140 end;
1142 with ItemProps[InsertRow(MsgPropTrSoundPan, IntToStr(Data.Pan), True)] do
1143 begin
1144 EditStyle := esSimple;
1145 MaxLength := 3;
1146 end;
1148 with ItemProps[InsertRow(MsgPropTrSoundCount, IntToStr(Data.PlayCount), True)] do
1149 begin
1150 EditStyle := esSimple;
1151 MaxLength := 3;
1152 end;
1154 with ItemProps[InsertRow(MsgPropTrSoundLocal, BoolNames[Data.Local], True)] do
1155 begin
1156 EditStyle := esPickList;
1157 ReadOnly := True;
1158 end;
1160 with ItemProps[InsertRow(MsgPropTrSoundSwitch, BoolNames[Data.SoundSwitch], True)] do
1161 begin
1162 EditStyle := esPickList;
1163 ReadOnly := True;
1164 end;
1165 end;
1167 TRIGGER_SPAWNMONSTER:
1168 begin
1169 with ItemProps[InsertRow(MsgPropTrMonsterType, MonsterToStr(Data.MonType), True)] do
1170 begin
1171 EditStyle := esEllipsis;
1172 ReadOnly := True;
1173 end;
1175 with ItemProps[InsertRow(MsgPropTrSpawnTo,
1176 Format('(%d:%d)', [Data.MonPos.X, Data.MonPos.Y]), True)] do
1177 begin
1178 EditStyle := esEllipsis;
1179 ReadOnly := True;
1180 end;
1182 with ItemProps[InsertRow(MsgPropDirection, DirNames[TDirection(Data.MonDir)], True)] do
1183 begin
1184 EditStyle := esPickList;
1185 ReadOnly := True;
1186 end;
1188 with ItemProps[InsertRow(MsgPropTrHealth, IntToStr(Data.MonHealth), True)] do
1189 begin
1190 EditStyle := esSimple;
1191 MaxLength := 5;
1192 end;
1194 with ItemProps[InsertRow(MsgPropTrMonsterActive, BoolNames[Data.MonActive], True)] do
1195 begin
1196 EditStyle := esPickList;
1197 ReadOnly := True;
1198 end;
1200 with ItemProps[InsertRow(MsgPropTrCount, IntToStr(Data.MonCount), True)] do
1201 begin
1202 EditStyle := esSimple;
1203 MaxLength := 5;
1204 end;
1206 with ItemProps[InsertRow(MsgPropTrFxType, EffectToStr(Data.MonEffect), True)] do
1207 begin
1208 EditStyle := esEllipsis;
1209 ReadOnly := True;
1210 end;
1212 with ItemProps[InsertRow(MsgPropTrSpawnMax, IntToStr(Data.MonMax), True)] do
1213 begin
1214 EditStyle := esSimple;
1215 MaxLength := 5;
1216 end;
1218 with ItemProps[InsertRow(MsgPropTrSpawnDelay, IntToStr(Data.MonDelay), True)] do
1219 begin
1220 EditStyle := esSimple;
1221 MaxLength := 5;
1222 end;
1224 case Data.MonBehav of
1225 1: str := MsgPropTrMonsterBehaviour1;
1226 2: str := MsgPropTrMonsterBehaviour2;
1227 3: str := MsgPropTrMonsterBehaviour3;
1228 4: str := MsgPropTrMonsterBehaviour4;
1229 5: str := MsgPropTrMonsterBehaviour5;
1230 else str := MsgPropTrMonsterBehaviour0;
1231 end;
1232 with ItemProps[InsertRow(MsgPropTrMonsterBehaviour, str, True)] do
1233 begin
1234 EditStyle := esPickList;
1235 ReadOnly := True;
1236 end;
1237 end;
1239 TRIGGER_SPAWNITEM:
1240 begin
1241 with ItemProps[InsertRow(MsgPropTrItemType, ItemToStr(Data.ItemType), True)] do
1242 begin
1243 EditStyle := esEllipsis;
1244 ReadOnly := True;
1245 end;
1247 with ItemProps[InsertRow(MsgPropTrSpawnTo,
1248 Format('(%d:%d)', [Data.ItemPos.X, Data.ItemPos.Y]), True)] do
1249 begin
1250 EditStyle := esEllipsis;
1251 ReadOnly := True;
1252 end;
1254 with ItemProps[InsertRow(MsgPropDmOnly, BoolNames[Data.ItemOnlyDM], True)] do
1255 begin
1256 EditStyle := esPickList;
1257 ReadOnly := True;
1258 end;
1260 with ItemProps[InsertRow(MsgPropItemFalls, BoolNames[Data.ItemFalls], True)] do
1261 begin
1262 EditStyle := esPickList;
1263 ReadOnly := True;
1264 end;
1266 with ItemProps[InsertRow(MsgPropTrCount, IntToStr(Data.ItemCount), True)] do
1267 begin
1268 EditStyle := esSimple;
1269 MaxLength := 5;
1270 end;
1272 with ItemProps[InsertRow(MsgPropTrFxType, EffectToStr(Data.ItemEffect), True)] do
1273 begin
1274 EditStyle := esEllipsis;
1275 ReadOnly := True;
1276 end;
1278 with ItemProps[InsertRow(MsgPropTrSpawnMax, IntToStr(Data.ItemMax), True)] do
1279 begin
1280 EditStyle := esSimple;
1281 MaxLength := 5;
1282 end;
1284 with ItemProps[InsertRow(MsgPropTrSpawnDelay, IntToStr(Data.ItemDelay), True)] do
1285 begin
1286 EditStyle := esSimple;
1287 MaxLength := 5;
1288 end;
1289 end;
1291 TRIGGER_MUSIC:
1292 begin
1293 str := win2utf(Data.MusicName);
1294 with ItemProps[InsertRow(MsgPropTrMusicName, str, True)] do
1295 begin
1296 EditStyle := esEllipsis;
1297 ReadOnly := True;
1298 end;
1300 if Data.MusicAction = 1 then
1301 str := MsgPropTrMusicOn
1302 else
1303 str := MsgPropTrMusicOff;
1305 with ItemProps[InsertRow(MsgPropTrMusicAct, str, True)] do
1306 begin
1307 EditStyle := esPickList;
1308 ReadOnly := True;
1309 end;
1310 end;
1312 TRIGGER_PUSH:
1313 begin
1314 with ItemProps[InsertRow(MsgPropTrPushAngle, IntToStr(Data.PushAngle), True)] do
1315 begin
1316 EditStyle := esSimple;
1317 MaxLength := 4;
1318 end;
1319 with ItemProps[InsertRow(MsgPropTrPushForce, IntToStr(Data.PushForce), True)] do
1320 begin
1321 EditStyle := esSimple;
1322 MaxLength := 4;
1323 end;
1324 with ItemProps[InsertRow(MsgPropTrPushReset, BoolNames[Data.ResetVel], True)] do
1325 begin
1326 EditStyle := esPickList;
1327 ReadOnly := True;
1328 end;
1329 end;
1331 TRIGGER_SCORE:
1332 begin
1333 case Data.ScoreAction of
1334 1: str := MsgPropTrScoreAct1;
1335 2: str := MsgPropTrScoreAct2;
1336 3: str := MsgPropTrScoreAct3;
1337 else str := MsgPropTrScoreAct0;
1338 end;
1339 with ItemProps[InsertRow(MsgPropTrScoreAct, str, True)] do
1340 begin
1341 EditStyle := esPickList;
1342 ReadOnly := True;
1343 end;
1344 with ItemProps[InsertRow(MsgPropTrCount, IntToStr(Data.ScoreCount), True)] do
1345 begin
1346 EditStyle := esSimple;
1347 MaxLength := 3;
1348 end;
1349 case Data.ScoreTeam of
1350 1: str := MsgPropTrScoreTeam1;
1351 2: str := MsgPropTrScoreTeam2;
1352 3: str := MsgPropTrScoreTeam3;
1353 else str := MsgPropTrScoreTeam0;
1354 end;
1355 with ItemProps[InsertRow(MsgPropTrScoreTeam, str, True)] do
1356 begin
1357 EditStyle := esPickList;
1358 ReadOnly := True;
1359 end;
1360 with ItemProps[InsertRow(MsgPropTrScoreCon, BoolNames[Data.ScoreCon], True)] do
1361 begin
1362 EditStyle := esPickList;
1363 ReadOnly := True;
1364 end;
1365 with ItemProps[InsertRow(MsgPropTrScoreMsg, BoolNames[Data.ScoreMsg], True)] do
1366 begin
1367 EditStyle := esPickList;
1368 ReadOnly := True;
1369 end;
1370 end;
1372 TRIGGER_MESSAGE:
1373 begin
1374 case Data.MessageKind of
1375 1: str := MsgPropTrMessageKind1;
1376 else str := MsgPropTrMessageKind0;
1377 end;
1378 with ItemProps[InsertRow(MsgPropTrMessageKind, str, True)] do
1379 begin
1380 EditStyle := esPickList;
1381 ReadOnly := True;
1382 end;
1383 case Data.MessageSendTo of
1384 1: str := MsgPropTrMessageTo1;
1385 2: str := MsgPropTrMessageTo2;
1386 3: str := MsgPropTrMessageTo3;
1387 4: str := MsgPropTrMessageTo4;
1388 5: str := MsgPropTrMessageTo5;
1389 else str := MsgPropTrMessageTo0;
1390 end;
1391 with ItemProps[InsertRow(MsgPropTrMessageTo, str, True)] do
1392 begin
1393 EditStyle := esPickList;
1394 ReadOnly := True;
1395 end;
1396 str := win2utf(Data.MessageText);
1397 with ItemProps[InsertRow(MsgPropTrMessageText, str, True)] do
1398 begin
1399 EditStyle := esSimple;
1400 MaxLength := 100;
1401 end;
1402 with ItemProps[InsertRow(MsgPropTrMessageTime, IntToStr(Data.MessageTime), True)] do
1403 begin
1404 EditStyle := esSimple;
1405 MaxLength := 5;
1406 end;
1407 end;
1409 TRIGGER_DAMAGE:
1410 begin
1411 with ItemProps[InsertRow(MsgPropTrDamageValue, IntToStr(Data.DamageValue), True)] do
1412 begin
1413 EditStyle := esSimple;
1414 MaxLength := 5;
1415 end;
1416 with ItemProps[InsertRow(MsgPropTrInterval, IntToStr(Data.DamageInterval), True)] do
1417 begin
1418 EditStyle := esSimple;
1419 MaxLength := 5;
1420 end;
1421 case Data.DamageKind of
1422 3: str := MsgPropTrDamageKind3;
1423 4: str := MsgPropTrDamageKind4;
1424 5: str := MsgPropTrDamageKind5;
1425 6: str := MsgPropTrDamageKind6;
1426 7: str := MsgPropTrDamageKind7;
1427 8: str := MsgPropTrDamageKind8;
1428 else str := MsgPropTrDamageKind0;
1429 end;
1430 with ItemProps[InsertRow(MsgPropTrDamageKind, str, True)] do
1431 begin
1432 EditStyle := esPickList;
1433 ReadOnly := True;
1434 end;
1435 end;
1437 TRIGGER_HEALTH:
1438 begin
1439 with ItemProps[InsertRow(MsgPropTrHealth, IntToStr(Data.HealValue), True)] do
1440 begin
1441 EditStyle := esSimple;
1442 MaxLength := 5;
1443 end;
1444 with ItemProps[InsertRow(MsgPropTrInterval, IntToStr(Data.HealInterval), True)] do
1445 begin
1446 EditStyle := esSimple;
1447 MaxLength := 5;
1448 end;
1449 with ItemProps[InsertRow(MsgPropTrHealthMax, BoolNames[Data.HealMax], True)] do
1450 begin
1451 EditStyle := esPickList;
1452 ReadOnly := True;
1453 end;
1454 with ItemProps[InsertRow(MsgPropTrSilent, BoolNames[Data.HealSilent], True)] do
1455 begin
1456 EditStyle := esPickList;
1457 ReadOnly := True;
1458 end;
1459 end;
1461 TRIGGER_SHOT:
1462 begin
1463 with ItemProps[InsertRow(MsgPropTrShotType, ShotToStr(Data.ShotType), True)] do
1464 begin
1465 EditStyle := esEllipsis;
1466 ReadOnly := True;
1467 end;
1469 with ItemProps[InsertRow(MsgPropTrShotSound, BoolNames[Data.ShotSound], True)] do
1470 begin
1471 EditStyle := esPickList;
1472 ReadOnly := True;
1473 end;
1475 with ItemProps[InsertRow(MsgPropTrShotPanel, IntToStr(Data.ShotPanelID), True)] do
1476 begin
1477 EditStyle := esEllipsis;
1478 ReadOnly := True;
1479 end;
1481 case Data.ShotTarget of
1482 1: str := MsgPropTrShotTo1;
1483 2: str := MsgPropTrShotTo2;
1484 3: str := MsgPropTrShotTo3;
1485 4: str := MsgPropTrShotTo4;
1486 5: str := MsgPropTrShotTo5;
1487 6: str := MsgPropTrShotTo6;
1488 else str := MsgPropTrShotTo0;
1489 end;
1490 with ItemProps[InsertRow(MsgPropTrShotTo, str, True)] do
1491 begin
1492 EditStyle := esPickList;
1493 ReadOnly := True;
1494 end;
1496 with ItemProps[InsertRow(MsgPropTrShotSight, IntToStr(Data.ShotIntSight), True)] do
1497 begin
1498 EditStyle := esSimple;
1499 MaxLength := 3;
1500 end;
1502 case Data.ShotAim of
1503 1: str := MsgPropTrShotAim1;
1504 2: str := MsgPropTrShotAim2;
1505 3: str := MsgPropTrShotAim3;
1506 else str := MsgPropTrShotAim0;
1507 end;
1508 with ItemProps[InsertRow(MsgPropTrShotAim, str, True)] do
1509 begin
1510 EditStyle := esPickList;
1511 ReadOnly := True;
1512 end;
1514 with ItemProps[InsertRow(MsgPropTrSpawnTo,
1515 Format('(%d:%d)', [Data.ShotPos.X, Data.ShotPos.Y]), True)] do
1516 begin
1517 EditStyle := esEllipsis;
1518 ReadOnly := True;
1519 end;
1521 with ItemProps[InsertRow(MsgPropTrShotAngle, IntToStr(Data.ShotAngle), True)] do
1522 begin
1523 EditStyle := esSimple;
1524 MaxLength := 4;
1525 end;
1527 with ItemProps[InsertRow(MsgPropTrExDelay, IntToStr(Data.ShotWait), True)] do
1528 begin
1529 EditStyle := esSimple;
1530 MaxLength := 5;
1531 end;
1533 with ItemProps[InsertRow(MsgPropTrShotAcc, IntToStr(Data.ShotAccuracy), True)] do
1534 begin
1535 EditStyle := esSimple;
1536 MaxLength := 5;
1537 end;
1539 with ItemProps[InsertRow(MsgPropTrShotAmmo, IntToStr(Data.ShotAmmo), True)] do
1540 begin
1541 EditStyle := esSimple;
1542 MaxLength := 5;
1543 end;
1545 with ItemProps[InsertRow(MsgPropTrShotReload, IntToStr(Data.ShotIntReload), True)] do
1546 begin
1547 EditStyle := esSimple;
1548 MaxLength := 4;
1549 end;
1550 end;
1552 TRIGGER_EFFECT:
1553 begin
1554 with ItemProps[InsertRow(MsgPropTrCount, IntToStr(Data.FXCount), True)] do
1555 begin
1556 EditStyle := esSimple;
1557 MaxLength := 3;
1558 end;
1560 if Data.FXType = 0 then
1561 str := MsgPropTrEffectParticle
1562 else
1563 str := MsgPropTrEffectAnimation;
1564 with ItemProps[InsertRow(MsgPropTrEffectType, str, True)] do
1565 begin
1566 EditStyle := esEllipsis;
1567 ReadOnly := True;
1568 end;
1570 str := '';
1571 if Data.FXType = 0 then
1572 case Data.FXSubType of
1573 TRIGGER_EFFECT_SLIQUID:
1574 str := MsgPropTrEffectSliquid;
1575 TRIGGER_EFFECT_LLIQUID:
1576 str := MsgPropTrEffectLliquid;
1577 TRIGGER_EFFECT_DLIQUID:
1578 str := MsgPropTrEffectDliquid;
1579 TRIGGER_EFFECT_BLOOD:
1580 str := MsgPropTrEffectBlood;
1581 TRIGGER_EFFECT_SPARK:
1582 str := MsgPropTrEffectSpark;
1583 TRIGGER_EFFECT_BUBBLE:
1584 str := MsgPropTrEffectBubble;
1585 end;
1586 if Data.FXType = 1 then
1587 begin
1588 if (Data.FXSubType = 0) or (Data.FXSubType > EFFECT_FIRE) then
1589 Data.FXSubType := EFFECT_TELEPORT;
1590 str := EffectToStr(Data.FXSubType);
1591 end;
1592 with ItemProps[InsertRow(MsgPropTrEffectSubtype, str, True)] do
1593 begin
1594 EditStyle := esEllipsis;
1595 ReadOnly := True;
1596 end;
1598 with ItemProps[InsertRow(MsgPropTrEffectColor, IntToStr(Data.FXColorR or (Data.FXColorG shl 8) or (Data.FXColorB shl 16)), True)] do
1599 begin
1600 EditStyle := esEllipsis;
1601 ReadOnly := True;
1602 end;
1604 with ItemProps[InsertRow(MsgPropTrEffectCenter, BoolNames[Data.FXPos = 0], True)] do
1605 begin
1606 EditStyle := esPickList;
1607 ReadOnly := True;
1608 end;
1610 with ItemProps[InsertRow(MsgPropTrExDelay, IntToStr(Data.FXWait), True)] do
1611 begin
1612 EditStyle := esSimple;
1613 MaxLength := 5;
1614 end;
1616 with ItemProps[InsertRow(MsgPropTrEffectVelx, IntToStr(Data.FXVelX), True)] do
1617 begin
1618 EditStyle := esSimple;
1619 MaxLength := 4;
1620 end;
1622 with ItemProps[InsertRow(MsgPropTrEffectVely, IntToStr(Data.FXVelY), True)] do
1623 begin
1624 EditStyle := esSimple;
1625 MaxLength := 4;
1626 end;
1628 with ItemProps[InsertRow(MsgPropTrEffectSpl, IntToStr(Data.FXSpreadL), True)] do
1629 begin
1630 EditStyle := esSimple;
1631 MaxLength := 3;
1632 end;
1634 with ItemProps[InsertRow(MsgPropTrEffectSpr, IntToStr(Data.FXSpreadR), True)] do
1635 begin
1636 EditStyle := esSimple;
1637 MaxLength := 3;
1638 end;
1640 with ItemProps[InsertRow(MsgPropTrEffectSpu, IntToStr(Data.FXSpreadU), True)] do
1641 begin
1642 EditStyle := esSimple;
1643 MaxLength := 3;
1644 end;
1646 with ItemProps[InsertRow(MsgPropTrEffectSpd, IntToStr(Data.FXSpreadD), True)] do
1647 begin
1648 EditStyle := esSimple;
1649 MaxLength := 3;
1650 end;
1651 end;
1652 end; //case TriggerType
1653 end;
1654 end; // OBJECT_TRIGGER:
1655 end;
1656 end;
1658 procedure ChangeShownProperty(Name: String; NewValue: String);
1659 var
1660 row: Integer;
1661 begin
1662 if SelectedObjectCount() <> 1 then
1663 Exit;
1664 if not SelectedObjects[GetFirstSelected()].Live then
1665 Exit;
1667 // Есть ли такой ключ:
1668 if MainForm.vleObjectProperty.FindRow(Name, row) then
1669 begin
1670 MainForm.vleObjectProperty.Values[Name] := NewValue;
1671 end;
1672 end;
1674 procedure SelectObject(fObjectType: Byte; fID: DWORD; Multi: Boolean);
1675 var
1676 a: Integer;
1677 b: Boolean;
1678 begin
1679 if Multi then
1680 begin
1681 b := False;
1683 // Уже выделен - убираем:
1684 if SelectedObjects <> nil then
1685 for a := 0 to High(SelectedObjects) do
1686 with SelectedObjects[a] do
1687 if Live and (ID = fID) and
1688 (ObjectType = fObjectType) then
1689 begin
1690 Live := False;
1691 b := True;
1692 end;
1694 if b then
1695 Exit;
1697 SetLength(SelectedObjects, Length(SelectedObjects)+1);
1699 with SelectedObjects[High(SelectedObjects)] do
1700 begin
1701 ObjectType := fObjectType;
1702 ID := fID;
1703 Live := True;
1704 end;
1705 end
1706 else // not Multi
1707 begin
1708 SetLength(SelectedObjects, 1);
1710 with SelectedObjects[0] do
1711 begin
1712 ObjectType := fObjectType;
1713 ID := fID;
1714 Live := True;
1715 end;
1716 end;
1718 MainForm.miCopy.Enabled := True;
1719 MainForm.miCut.Enabled := True;
1721 if fObjectType = OBJECT_PANEL then
1722 begin
1723 MainForm.miToFore.Enabled := True;
1724 MainForm.miToBack.Enabled := True;
1725 end;
1726 end;
1728 procedure RemoveSelectFromObjects();
1729 begin
1730 SelectedObjects := nil;
1731 DrawPressRect := False;
1732 MouseLDown := False;
1733 MouseRDown := False;
1734 MouseAction := MOUSEACTION_NONE;
1735 SelectFlag := SELECTFLAG_NONE;
1736 ResizeType := RESIZETYPE_NONE;
1737 ResizeDirection := RESIZEDIR_NONE;
1739 MainForm.vleObjectProperty.Strings.Clear();
1741 MainForm.miCopy.Enabled := False;
1742 MainForm.miCut.Enabled := False;
1743 MainForm.miToFore.Enabled := False;
1744 MainForm.miToBack.Enabled := False;
1745 end;
1747 procedure DeleteSelectedObjects();
1748 var
1749 i, a, ii: Integer;
1750 b: Boolean;
1751 begin
1752 if SelectedObjects = nil then
1753 Exit;
1755 b := False;
1756 i := 0;
1758 for a := 0 to High(SelectedObjects) do
1759 with SelectedObjects[a] do
1760 if Live then
1761 begin
1762 if not b then
1763 begin
1764 SetLength(UndoBuffer, Length(UndoBuffer)+1);
1765 i := High(UndoBuffer);
1766 b := True;
1767 end;
1769 SetLength(UndoBuffer[i], Length(UndoBuffer[i])+1);
1770 ii := High(UndoBuffer[i]);
1772 case ObjectType of
1773 OBJECT_PANEL:
1774 begin
1775 UndoBuffer[i, ii].UndoType := UNDO_DELETE_PANEL;
1776 New(UndoBuffer[i, ii].Panel);
1777 UndoBuffer[i, ii].Panel^ := gPanels[ID];
1778 end;
1779 OBJECT_ITEM:
1780 begin
1781 UndoBuffer[i, ii].UndoType := UNDO_DELETE_ITEM;
1782 UndoBuffer[i, ii].Item := gItems[ID];
1783 end;
1784 OBJECT_AREA:
1785 begin
1786 UndoBuffer[i, ii].UndoType := UNDO_DELETE_AREA;
1787 UndoBuffer[i, ii].Area := gAreas[ID];
1788 end;
1789 OBJECT_TRIGGER:
1790 begin
1791 UndoBuffer[i, ii].UndoType := UNDO_DELETE_TRIGGER;
1792 UndoBuffer[i, ii].Trigger := gTriggers[ID];
1793 end;
1794 end;
1796 RemoveObject(ID, ObjectType);
1797 end;
1799 RemoveSelectFromObjects();
1801 MainForm.miUndo.Enabled := UndoBuffer <> nil;
1802 MainForm.RecountSelectedObjects();
1803 end;
1805 procedure Undo_Add(ObjectType: Byte; ID: DWORD; Group: Boolean = False);
1806 var
1807 i, ii: Integer;
1808 begin
1809 if (not Group) or (Length(UndoBuffer) = 0) then
1810 SetLength(UndoBuffer, Length(UndoBuffer)+1);
1811 SetLength(UndoBuffer[High(UndoBuffer)], Length(UndoBuffer[High(UndoBuffer)])+1);
1812 i := High(UndoBuffer);
1813 ii := High(UndoBuffer[i]);
1815 case ObjectType of
1816 OBJECT_PANEL:
1817 UndoBuffer[i, ii].UndoType := UNDO_ADD_PANEL;
1818 OBJECT_ITEM:
1819 UndoBuffer[i, ii].UndoType := UNDO_ADD_ITEM;
1820 OBJECT_MONSTER:
1821 UndoBuffer[i, ii].UndoType := UNDO_ADD_MONSTER;
1822 OBJECT_AREA:
1823 UndoBuffer[i, ii].UndoType := UNDO_ADD_AREA;
1824 OBJECT_TRIGGER:
1825 UndoBuffer[i, ii].UndoType := UNDO_ADD_TRIGGER;
1826 end;
1828 UndoBuffer[i, ii].AddID := ID;
1830 MainForm.miUndo.Enabled := UndoBuffer <> nil;
1831 end;
1833 procedure FullClear();
1834 begin
1835 RemoveSelectFromObjects();
1836 ClearMap();
1837 LoadSky(gMapInfo.SkyName);
1838 UndoBuffer := nil;
1839 slInvalidTextures.Clear();
1840 MapCheckForm.lbErrorList.Clear();
1841 MapCheckForm.mErrorDescription.Clear();
1843 MainForm.miUndo.Enabled := False;
1844 MainForm.sbHorizontal.Position := 0;
1845 MainForm.sbVertical.Position := 0;
1846 MainForm.FormResize(nil);
1847 MainForm.Caption := FormCaption;
1848 OpenedMap := '';
1849 OpenedWAD := '';
1850 end;
1852 procedure ErrorMessageBox(str: String);
1853 begin
1854 Application.MessageBox(PChar(str), PChar(MsgMsgError),
1855 MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1);
1856 end;
1858 function CheckProperty(): Boolean;
1859 var
1860 _id: Integer;
1861 begin
1862 Result := False;
1864 _id := GetFirstSelected();
1866 if SelectedObjects[_id].ObjectType = OBJECT_PANEL then
1867 with gPanels[SelectedObjects[_id].ID] do
1868 begin
1869 if TextureWidth <> 0 then
1870 if StrToIntDef(MainForm.vleObjectProperty.Values[MsgPropWidth], 1) mod TextureWidth <> 0 then
1871 begin
1872 ErrorMessageBox(Format(MsgMsgWrongTexwidth,
1873 [TextureWidth]));
1874 Exit;
1875 end;
1877 if TextureHeight <> 0 then
1878 if StrToIntDef(Trim(MainForm.vleObjectProperty.Values[MsgPropHeight]), 1) mod TextureHeight <> 0 then
1879 begin
1880 ErrorMessageBox(Format(MsgMsgWrongTexheight,
1881 [TextureHeight]));
1882 Exit;
1883 end;
1885 if IsTexturedPanel(PanelType) and (TextureName <> '') then
1886 if not (StrToIntDef(MainForm.vleObjectProperty.Values[MsgPropPanelAlpha], -1) in [0..255]) then
1887 begin
1888 ErrorMessageBox(MsgMsgWrongAlpha);
1889 Exit;
1890 end;
1891 end;
1893 if SelectedObjects[_id].ObjectType in [OBJECT_PANEL, OBJECT_TRIGGER] then
1894 if (StrToIntDef(MainForm.vleObjectProperty.Values[MsgPropWidth], 0) <= 0) or
1895 (StrToIntDef(MainForm.vleObjectProperty.Values[MsgPropHeight], 0) <= 0) then
1896 begin
1897 ErrorMessageBox(MsgMsgWrongSize);
1898 Exit;
1899 end;
1901 if (Trim(MainForm.vleObjectProperty.Values[MsgPropX]) = '') or
1902 (Trim(MainForm.vleObjectProperty.Values[MsgPropY]) = '') then
1903 begin
1904 ErrorMessageBox(MsgMsgWrongXy);
1905 Exit;
1906 end;
1908 Result := True;
1909 end;
1911 procedure SelectTexture(ID: Integer);
1912 begin
1913 MainForm.lbTextureList.ItemIndex := ID;
1914 MainForm.lbTextureListClick(nil);
1915 end;
1917 function AddTexture(aWAD, aSection, aTex: String; silent: Boolean): Boolean;
1918 var
1919 a, FrameLen: Integer;
1920 ok: Boolean;
1921 FileName: String;
1922 ResourceName: String;
1923 FullResourceName: String;
1924 SectionName: String;
1925 Data: Pointer;
1926 Width, Height: Word;
1927 fn: String;
1928 begin
1929 Data := nil;
1930 FrameLen := 0;
1931 Width := 0;
1932 Height := 0;
1934 if aSection = '..' then
1935 SectionName := ''
1936 else
1937 SectionName := aSection;
1939 if aWAD = '' then
1940 aWAD := MsgWadSpecialMap;
1942 if aWAD = MsgWadSpecialMap then
1943 begin // Файл карты
1944 g_ProcessResourceStr(OpenedMap, @fn, nil, nil);
1945 FileName := fn;
1946 ResourceName := ':'+SectionName+'\'+aTex;
1947 end
1948 else
1949 if aWAD = MsgWadSpecialTexs then
1950 begin // Спец. текстуры
1951 FileName := '';
1952 ResourceName := aTex;
1953 end
1954 else
1955 begin // Внешний WAD
1956 FileName := WadsDir + DirectorySeparator + aWAD;
1957 ResourceName := aWAD+':'+SectionName+'\'+aTex;
1958 end;
1960 ok := True;
1962 // Есть ли уже такая текстура:
1963 for a := 0 to MainForm.lbTextureList.Items.Count-1 do
1964 if ResourceName = MainForm.lbTextureList.Items[a] then
1965 begin
1966 if not silent then
1967 ErrorMessageBox(Format(MsgMsgTextureAlready,
1968 [ResourceName]));
1969 ok := False;
1970 end;
1972 // Название ресурса <= 64 символов:
1973 if Length(ResourceName) > 64 then
1974 begin
1975 if not silent then
1976 ErrorMessageBox(Format(MsgMsgResName64,
1977 [ResourceName]));
1978 ok := False;
1979 end;
1981 if ok then
1982 begin
1983 a := -1;
1984 if aWAD = MsgWadSpecialTexs then
1985 begin
1986 a := MainForm.lbTextureList.Items.Add(ResourceName);
1987 if not silent then
1988 SelectTexture(a);
1989 Result := True;
1990 Exit;
1991 end;
1993 FullResourceName := FileName+':'+SectionName+'\'+aTex;
1995 if IsAnim(FullResourceName) then
1996 begin // Аним. текстура
1997 GetFrame(FullResourceName, Data, FrameLen, Width, Height);
1999 if not g_CreateTextureMemorySize(Data, FrameLen, ResourceName, 0, 0, Width, Height, 1) then
2000 ok := False;
2001 a := MainForm.lbTextureList.Items.Add(ResourceName);
2002 end
2003 else // Обычная текстура
2004 begin
2005 if not g_CreateTextureWAD(ResourceName, FullResourceName) then
2006 ok := False;
2007 a := MainForm.lbTextureList.Items.Add(ResourceName);
2008 end;
2009 if (not ok) and (slInvalidTextures.IndexOf(ResourceName) = -1) then
2010 begin
2011 slInvalidTextures.Add(ResourceName);
2012 ok := True;
2013 end;
2014 if (a > -1) and (not silent) then
2015 SelectTexture(a);
2016 end;
2018 Result := ok;
2019 end;
2021 procedure UpdateCaption(sMap, sFile, sRes: String);
2022 begin
2023 with MainForm do
2024 if (sFile = '') and (sRes = '') and (sMap = '') then
2025 Caption := FormCaption
2026 else
2027 if sMap = '' then
2028 Caption := Format('%s - %s:%s', [FormCaption, sFile, sRes])
2029 else
2030 if (sFile <> '') and (sRes <> '') then
2031 Caption := Format('%s - %s (%s:%s)', [FormCaption, sMap, sFile, sRes])
2032 else
2033 Caption := Format('%s - %s', [FormCaption, sMap]);
2034 end;
2036 procedure OpenMap(FileName: String; mapN: String);
2037 var
2038 MapName: String;
2039 idx: Integer;
2040 begin
2041 SelectMapForm.Caption := MsgCapOpen;
2042 SelectMapForm.GetMaps(FileName);
2044 if (FileName = OpenedWAD) and
2045 (OpenedMap <> '') then
2046 begin
2047 MapName := OpenedMap;
2048 while (Pos(':\', MapName) > 0) do
2049 Delete(MapName, 1, Pos(':\', MapName) + 1);
2051 idx := SelectMapForm.lbMapList.Items.IndexOf(MapName);
2052 SelectMapForm.lbMapList.ItemIndex := idx;
2053 end
2054 else
2055 if SelectMapForm.lbMapList.Count > 0 then
2056 SelectMapForm.lbMapList.ItemIndex := 0
2057 else
2058 SelectMapForm.lbMapList.ItemIndex := -1;
2060 if mapN = '' then
2061 idx := -1
2062 else
2063 idx := SelectMapForm.lbMapList.Items.IndexOf(mapN);
2065 if idx < 0 then
2066 begin
2067 if (SelectMapForm.ShowModal() = mrOK) and
2068 (SelectMapForm.lbMapList.ItemIndex <> -1) then
2069 idx := SelectMapForm.lbMapList.ItemIndex
2070 else
2071 Exit;
2072 end;
2074 MapName := SelectMapForm.lbMapList.Items[idx];
2076 with MainForm do
2077 begin
2078 FullClear();
2080 pLoadProgress.Left := (RenderPanel.Width div 2)-(pLoadProgress.Width div 2);
2081 pLoadProgress.Top := (RenderPanel.Height div 2)-(pLoadProgress.Height div 2);
2082 pLoadProgress.Show();
2084 OpenedMap := FileName+':\'+MapName;
2085 OpenedWAD := FileName;
2087 idx := RecentFiles.IndexOf(OpenedMap);
2088 // Такая карта уже недавно открывалась:
2089 if idx >= 0 then
2090 RecentFiles.Delete(idx);
2091 RecentFiles.Insert(0, OpenedMap);
2092 RefreshRecentMenu();
2094 LoadMap(OpenedMap);
2096 pLoadProgress.Hide();
2097 FormResize(nil);
2099 lbTextureList.Sorted := True;
2100 lbTextureList.Sorted := False;
2102 UpdateCaption(gMapInfo.Name, ExtractFileName(FileName), MapName);
2103 end;
2104 end;
2106 procedure MoveSelectedObjects(Wall, alt: Boolean; dx, dy: Integer);
2107 var
2108 okX, okY: Boolean;
2109 a: Integer;
2110 begin
2111 if SelectedObjects = nil then
2112 Exit;
2114 okX := True;
2115 okY := True;
2117 if Wall then
2118 for a := 0 to High(SelectedObjects) do
2119 if SelectedObjects[a].Live then
2120 begin
2121 if ObjectCollideLevel(SelectedObjects[a].ID, SelectedObjects[a].ObjectType, dx, 0) then
2122 okX := False;
2124 if ObjectCollideLevel(SelectedObjects[a].ID, SelectedObjects[a].ObjectType, 0, dy) then
2125 okY := False;
2127 if (not okX) or (not okY) then
2128 Break;
2129 end;
2131 if okX or okY then
2132 begin
2133 for a := 0 to High(SelectedObjects) do
2134 if SelectedObjects[a].Live then
2135 begin
2136 if okX then
2137 MoveObject(SelectedObjects[a].ObjectType, SelectedObjects[a].ID, dx, 0);
2139 if okY then
2140 MoveObject(SelectedObjects[a].ObjectType, SelectedObjects[a].ID, 0, dy);
2142 if alt and (SelectedObjects[a].ObjectType = OBJECT_TRIGGER) then
2143 begin
2144 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_PRESS,
2145 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
2146 begin // Двигаем зону Расширителя
2147 if okX then
2148 gTriggers[SelectedObjects[a].ID].Data.tX := gTriggers[SelectedObjects[a].ID].Data.tX+dx;
2149 if okY then
2150 gTriggers[SelectedObjects[a].ID].Data.tY := gTriggers[SelectedObjects[a].ID].Data.tY+dy;
2151 end;
2153 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_TELEPORT] then
2154 begin // Двигаем точку назначения Телепорта
2155 if okX then
2156 gTriggers[SelectedObjects[a].ID].Data.TargetPoint.X := gTriggers[SelectedObjects[a].ID].Data.TargetPoint.X+dx;
2157 if okY then
2158 gTriggers[SelectedObjects[a].ID].Data.TargetPoint.Y := gTriggers[SelectedObjects[a].ID].Data.TargetPoint.Y+dy;
2159 end;
2161 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_SPAWNMONSTER] then
2162 begin // Двигаем точку создания монстра
2163 if okX then
2164 gTriggers[SelectedObjects[a].ID].Data.MonPos.X := gTriggers[SelectedObjects[a].ID].Data.MonPos.X+dx;
2165 if okY then
2166 gTriggers[SelectedObjects[a].ID].Data.MonPos.Y := gTriggers[SelectedObjects[a].ID].Data.MonPos.Y+dy;
2167 end;
2169 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_SPAWNITEM] then
2170 begin // Двигаем точку создания предмета
2171 if okX then
2172 gTriggers[SelectedObjects[a].ID].Data.ItemPos.X := gTriggers[SelectedObjects[a].ID].Data.ItemPos.X+dx;
2173 if okY then
2174 gTriggers[SelectedObjects[a].ID].Data.ItemPos.Y := gTriggers[SelectedObjects[a].ID].Data.ItemPos.Y+dy;
2175 end;
2177 if gTriggers[SelectedObjects[a].ID].TriggerType in [TRIGGER_SHOT] then
2178 begin // Двигаем точку создания выстрела
2179 if okX then
2180 gTriggers[SelectedObjects[a].ID].Data.ShotPos.X := gTriggers[SelectedObjects[a].ID].Data.ShotPos.X+dx;
2181 if okY then
2182 gTriggers[SelectedObjects[a].ID].Data.ShotPos.Y := gTriggers[SelectedObjects[a].ID].Data.ShotPos.Y+dy;
2183 end;
2184 end;
2185 end;
2187 LastMovePoint := MousePos;
2188 end;
2189 end;
2191 procedure ShowLayer(Layer: Byte; show: Boolean);
2192 begin
2193 LayerEnabled[Layer] := show;
2195 case Layer of
2196 LAYER_BACK:
2197 begin
2198 MainForm.miLayer1.Checked := show;
2199 MainForm.miLayerP1.Checked := show;
2200 end;
2201 LAYER_WALLS:
2202 begin
2203 MainForm.miLayer2.Checked := show;
2204 MainForm.miLayerP2.Checked := show;
2205 end;
2206 LAYER_FOREGROUND:
2207 begin
2208 MainForm.miLayer3.Checked := show;
2209 MainForm.miLayerP3.Checked := show;
2210 end;
2211 LAYER_STEPS:
2212 begin
2213 MainForm.miLayer4.Checked := show;
2214 MainForm.miLayerP4.Checked := show;
2215 end;
2216 LAYER_WATER:
2217 begin
2218 MainForm.miLayer5.Checked := show;
2219 MainForm.miLayerP5.Checked := show;
2220 end;
2221 LAYER_ITEMS:
2222 begin
2223 MainForm.miLayer6.Checked := show;
2224 MainForm.miLayerP6.Checked := show;
2225 end;
2226 LAYER_MONSTERS:
2227 begin
2228 MainForm.miLayer7.Checked := show;
2229 MainForm.miLayerP7.Checked := show;
2230 end;
2231 LAYER_AREAS:
2232 begin
2233 MainForm.miLayer8.Checked := show;
2234 MainForm.miLayerP8.Checked := show;
2235 end;
2236 LAYER_TRIGGERS:
2237 begin
2238 MainForm.miLayer9.Checked := show;
2239 MainForm.miLayerP9.Checked := show;
2240 end;
2241 end;
2243 RemoveSelectFromObjects();
2244 end;
2246 procedure SwitchLayer(Layer: Byte);
2247 begin
2248 ShowLayer(Layer, not LayerEnabled[Layer]);
2249 end;
2251 procedure SwitchMap();
2252 begin
2253 ShowMap := not ShowMap;
2254 MainForm.tbShowMap.Down := ShowMap;
2255 MainForm.miMiniMap.Checked := ShowMap;
2256 end;
2258 procedure ShowEdges();
2259 begin
2260 if drEdge[3] < 255 then
2261 drEdge[3] := 255
2262 else
2263 drEdge[3] := gAlphaEdge;
2264 MainForm.miShowEdges.Checked := drEdge[3] <> 255;
2265 end;
2267 function SelectedTexture(): String;
2268 begin
2269 if MainForm.lbTextureList.ItemIndex <> -1 then
2270 Result := MainForm.lbTextureList.Items[MainForm.lbTextureList.ItemIndex]
2271 else
2272 Result := '';
2273 end;
2275 function IsSpecialTextureSel(): Boolean;
2276 begin
2277 Result := (MainForm.lbTextureList.ItemIndex <> -1) and
2278 IsSpecialTexture(MainForm.lbTextureList.Items[MainForm.lbTextureList.ItemIndex]);
2279 end;
2281 function CopyBufferToString(var CopyBuf: TCopyRecArray): String;
2282 var
2283 i, j: Integer;
2284 Res: String;
2286 procedure AddInt(x: Integer);
2287 begin
2288 Res := Res + IntToStr(x) + ' ';
2289 end;
2291 begin
2292 Result := '';
2294 if Length(CopyBuf) = 0 then
2295 Exit;
2297 Res := CLIPBOARD_SIG + ' ';
2299 for i := 0 to High(CopyBuf) do
2300 begin
2301 if (CopyBuf[i].ObjectType = OBJECT_PANEL) and
2302 (CopyBuf[i].Panel = nil) then
2303 Continue;
2305 // Тип объекта:
2306 AddInt(CopyBuf[i].ObjectType);
2307 Res := Res + '; ';
2309 // Свойства объекта:
2310 case CopyBuf[i].ObjectType of
2311 OBJECT_PANEL:
2312 with CopyBuf[i].Panel^ do
2313 begin
2314 AddInt(PanelType);
2315 AddInt(X);
2316 AddInt(Y);
2317 AddInt(Width);
2318 AddInt(Height);
2319 Res := Res + '"' + TextureName + '" ';
2320 AddInt(Alpha);
2321 AddInt(IfThen(Blending, 1, 0));
2322 end;
2324 OBJECT_ITEM:
2325 with CopyBuf[i].Item do
2326 begin
2327 AddInt(ItemType);
2328 AddInt(X);
2329 AddInt(Y);
2330 AddInt(IfThen(OnlyDM, 1, 0));
2331 AddInt(IfThen(Fall, 1, 0));
2332 end;
2334 OBJECT_MONSTER:
2335 with CopyBuf[i].Monster do
2336 begin
2337 AddInt(MonsterType);
2338 AddInt(X);
2339 AddInt(Y);
2340 AddInt(IfThen(Direction = D_LEFT, 1, 0));
2341 end;
2343 OBJECT_AREA:
2344 with CopyBuf[i].Area do
2345 begin
2346 AddInt(AreaType);
2347 AddInt(X);
2348 AddInt(Y);
2349 AddInt(IfThen(Direction = D_LEFT, 1, 0));
2350 end;
2352 OBJECT_TRIGGER:
2353 with CopyBuf[i].Trigger do
2354 begin
2355 AddInt(TriggerType);
2356 AddInt(X);
2357 AddInt(Y);
2358 AddInt(Width);
2359 AddInt(Height);
2360 AddInt(ActivateType);
2361 AddInt(Key);
2362 AddInt(IfThen(Enabled, 1, 0));
2363 AddInt(TexturePanel);
2365 for j := 0 to 127 do
2366 AddInt(Data.Default[j]);
2367 end;
2368 end;
2369 end;
2371 Result := Res;
2372 end;
2374 procedure StringToCopyBuffer(Str: String; var CopyBuf: TCopyRecArray;
2375 var pmin: TPoint);
2376 var
2377 i, j, t: Integer;
2379 function GetNext(): String;
2380 var
2381 p: Integer;
2383 begin
2384 if Str[1] = '"' then
2385 begin
2386 Delete(Str, 1, 1);
2387 p := Pos('"', Str);
2389 if p = 0 then
2390 begin
2391 Result := Str;
2392 Str := '';
2393 end
2394 else
2395 begin
2396 Result := Copy(Str, 1, p-1);
2397 Delete(Str, 1, p);
2398 Str := Trim(Str);
2399 end;
2400 end
2401 else
2402 begin
2403 p := Pos(' ', Str);
2405 if p = 0 then
2406 begin
2407 Result := Str;
2408 Str := '';
2409 end
2410 else
2411 begin
2412 Result := Copy(Str, 1, p-1);
2413 Delete(Str, 1, p);
2414 Str := Trim(Str);
2415 end;
2416 end;
2417 end;
2419 begin
2420 Str := Trim(Str);
2422 if GetNext() <> CLIPBOARD_SIG then
2423 Exit;
2425 while Str <> '' do
2426 begin
2427 // Тип объекта:
2428 t := StrToIntDef(GetNext(), 0);
2430 if (t < OBJECT_PANEL) or (t > OBJECT_TRIGGER) or
2431 (GetNext() <> ';') then
2432 begin // Что-то не то => пропускаем:
2433 t := Pos(';', Str);
2434 Delete(Str, 1, t);
2435 Str := Trim(Str);
2437 Continue;
2438 end;
2440 i := Length(CopyBuf);
2441 SetLength(CopyBuf, i + 1);
2443 CopyBuf[i].ObjectType := t;
2444 CopyBuf[i].Panel := nil;
2446 // Свойства объекта:
2447 case t of
2448 OBJECT_PANEL:
2449 begin
2450 New(CopyBuf[i].Panel);
2452 with CopyBuf[i].Panel^ do
2453 begin
2454 PanelType := StrToIntDef(GetNext(), PANEL_WALL);
2455 X := StrToIntDef(GetNext(), 0);
2456 Y := StrToIntDef(GetNext(), 0);
2457 pmin.X := Min(X, pmin.X);
2458 pmin.Y := Min(Y, pmin.Y);
2459 Width := StrToIntDef(GetNext(), 16);
2460 Height := StrToIntDef(GetNext(), 16);
2461 TextureName := GetNext();
2462 Alpha := StrToIntDef(GetNext(), 0);
2463 Blending := (GetNext() = '1');
2464 end;
2465 end;
2467 OBJECT_ITEM:
2468 with CopyBuf[i].Item do
2469 begin
2470 ItemType := StrToIntDef(GetNext(), ITEM_MEDKIT_SMALL);
2471 X := StrToIntDef(GetNext(), 0);
2472 Y := StrToIntDef(GetNext(), 0);
2473 pmin.X := Min(X, pmin.X);
2474 pmin.Y := Min(Y, pmin.Y);
2475 OnlyDM := (GetNext() = '1');
2476 Fall := (GetNext() = '1');
2477 end;
2479 OBJECT_MONSTER:
2480 with CopyBuf[i].Monster do
2481 begin
2482 MonsterType := StrToIntDef(GetNext(), MONSTER_DEMON);
2483 X := StrToIntDef(GetNext(), 0);
2484 Y := StrToIntDef(GetNext(), 0);
2485 pmin.X := Min(X, pmin.X);
2486 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_AREA:
2495 with CopyBuf[i].Area do
2496 begin
2497 AreaType := StrToIntDef(GetNext(), AREA_PLAYERPOINT1);
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 if GetNext() = '1' then
2503 Direction := D_LEFT
2504 else
2505 Direction := D_RIGHT;
2506 end;
2508 OBJECT_TRIGGER:
2509 with CopyBuf[i].Trigger do
2510 begin
2511 TriggerType := StrToIntDef(GetNext(), TRIGGER_EXIT);
2512 X := StrToIntDef(GetNext(), 0);
2513 Y := StrToIntDef(GetNext(), 0);
2514 pmin.X := Min(X, pmin.X);
2515 pmin.Y := Min(Y, pmin.Y);
2516 Width := StrToIntDef(GetNext(), 16);
2517 Height := StrToIntDef(GetNext(), 16);
2518 ActivateType := StrToIntDef(GetNext(), 0);
2519 Key := StrToIntDef(GetNext(), 0);
2520 Enabled := (GetNext() = '1');
2521 TexturePanel := StrToIntDef(GetNext(), 0);
2523 for j := 0 to 127 do
2524 Data.Default[j] := StrToIntDef(GetNext(), 0);
2526 case TriggerType of
2527 TRIGGER_TELEPORT:
2528 begin
2529 pmin.X := Min(Data.TargetPoint.X, pmin.X);
2530 pmin.Y := Min(Data.TargetPoint.Y, pmin.Y);
2531 end;
2532 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
2533 begin
2534 pmin.X := Min(Data.tX, pmin.X);
2535 pmin.Y := Min(Data.tY, pmin.Y);
2536 end;
2537 TRIGGER_SPAWNMONSTER:
2538 begin
2539 pmin.X := Min(Data.MonPos.X, pmin.X);
2540 pmin.Y := Min(Data.MonPos.Y, pmin.Y);
2541 end;
2542 TRIGGER_SPAWNITEM:
2543 begin
2544 pmin.X := Min(Data.ItemPos.X, pmin.X);
2545 pmin.Y := Min(Data.ItemPos.Y, pmin.Y);
2546 end;
2547 TRIGGER_SHOT:
2548 begin
2549 pmin.X := Min(Data.ShotPos.X, pmin.X);
2550 pmin.Y := Min(Data.ShotPos.Y, pmin.Y);
2551 end;
2552 end;
2553 end;
2554 end;
2555 end;
2556 end;
2558 //----------------------------------------
2559 //Закончились вспомогательные процедуры
2560 //----------------------------------------
2562 type
2563 TRecentHandler = class
2564 private
2565 FForm: TMainForm;
2566 FPath: String;
2567 public
2568 constructor Create (form: TMainForm; path: String);
2569 procedure Execute (Sender: TObject);
2570 end;
2572 constructor TRecentHandler.Create (form: TMainForm; path: String);
2573 begin
2574 Assert(form <> nil);
2575 FForm := form;
2576 FPath := path;
2577 end;
2579 procedure TRecentHandler.Execute (Sender: TObject);
2580 var fn: AnsiString;
2581 begin
2582 fn := g_ExtractWadName(FPath);
2583 if FileExists(fn) then
2584 OpenMap(fn, g_ExtractFilePathName(FPath))
2585 else
2586 Application.MessageBox('', 'File not available anymore', MB_OK);
2587 // if Application.MessageBox(PChar(MsgMsgDelRecentPromt), PChar(MsgMsgDelRecent), MB_ICONQUESTION or MB_YESNO) = idYes then
2588 // begin
2589 // RecentFiles.Delete(n);
2590 // RefreshRecentMenu();
2591 // end;
2592 end;
2594 procedure TMainForm.RefillRecentMenu (menu: TMenuItem; start: Integer; fmt: AnsiString);
2595 var i: Integer; MI: TMenuItem; cb: TMethod; h: TRecentHandler; s: AnsiString;
2596 begin
2597 Assert(menu <> nil);
2598 Assert(start >= 0);
2599 Assert(start <= menu.Count);
2601 // clear all recent entries from menu
2602 i := start;
2603 while i < menu.Count do
2604 begin
2605 MI := menu.Items[i];
2606 cb := TMethod(MI.OnClick);
2607 if cb.Code = @TRecentHandler.Execute then
2608 begin
2609 // this is recent menu entry
2610 // remove it and free callback handler
2611 h := TRecentHandler(cb.Data);
2612 menu.Delete(i);
2613 MI.Free();
2614 h.Free();
2615 end
2616 else
2617 Inc(i);
2618 end;
2620 // fill with a new ones
2621 for i := 0 to RecentFiles.Count - 1 do
2622 begin
2623 s := RecentFiles[i];
2624 h := TRecentHandler.Create(self, s);
2625 MI := TMenuItem.Create(menu);
2626 MI.Caption := Format(fmt, [i + 1, g_ExtractWadNameNoPath(s), g_ExtractFilePathName(s)]);
2627 MI.OnClick := h.Execute;
2628 menu.Insert(start + i, MI);
2629 end;
2630 end;
2632 procedure TMainForm.RefreshRecentMenu();
2633 var start: Integer;
2634 begin
2635 while RecentFiles.Count > RecentCount do
2636 RecentFiles.Delete(RecentFiles.Count - 1);
2638 if miMacRecentSubMenu.Visible then
2639 begin
2640 // Reconstruct OSX-like recent list
2641 RefillRecentMenu(miMacRecentSubMenu, 0, '%1:s - %2:s');
2642 miMacRecentEnd.Enabled := RecentFiles.Count <> 0;
2643 miMacRecentEnd.Visible := RecentFiles.Count <> 0;
2644 end;
2646 if miWinRecentStart.Visible then
2647 begin
2648 // Reconstruct Windows-like recent list
2649 start := miMenuFile.IndexOf(miWinRecent);
2650 if start < 0 then start := miMenuFile.Count else start := start + 1;
2651 RefillRecentMenu(miMenuFile, start, '%0:d %1:s:%2:s');
2652 miWinRecent.Enabled := False;
2653 miWinRecent.Visible := RecentFiles.Count = 0;
2654 end;
2655 end;
2657 procedure TMainForm.miMacRecentClearClick(Sender: TObject);
2658 begin
2659 RecentFiles.Clear();
2660 RefreshRecentMenu();
2661 end;
2663 procedure TMainForm.aEditorOptionsExecute(Sender: TObject);
2664 begin
2665 OptionsForm.ShowModal();
2666 end;
2668 procedure LoadStdFont(cfgres, texture: string; var FontID: DWORD);
2669 var
2670 cwdt, chgt: Byte;
2671 spc: ShortInt;
2672 ID: DWORD;
2673 cfgdata: Pointer;
2674 cfglen: Integer;
2675 config: TConfig;
2676 begin
2677 ID := 0;
2678 g_ReadResource(GameWad, 'FONTS', cfgres, cfgdata, cfglen);
2679 if cfgdata <> nil then
2680 begin
2681 if not g_CreateTextureWAD('FONT_STD', GameWad + ':FONTS\' + texture) then
2682 e_WriteLog('ERROR ERROR ERROR', MSG_WARNING);
2684 config := TConfig.CreateMem(cfgdata, cfglen);
2685 cwdt := Min(Max(config.ReadInt('FontMap', 'CharWidth', 0), 0), 255);
2686 chgt := Min(Max(config.ReadInt('FontMap', 'CharHeight', 0), 0), 255);
2687 spc := Min(Max(config.ReadInt('FontMap', 'Kerning', 0), -128), 127);
2689 if g_GetTexture('FONT_STD', ID) then
2690 e_TextureFontBuild(ID, FontID, cwdt, chgt, spc - 2);
2692 config.Free();
2693 FreeMem(cfgdata)
2694 end
2695 else
2696 begin
2697 e_WriteLog('Could not load FONT_STD', MSG_WARNING)
2698 end
2699 end;
2701 procedure TMainForm.FormCreate(Sender: TObject);
2702 var
2703 config: TConfig;
2704 i: Integer;
2705 s: String;
2706 begin
2707 Randomize();
2709 {$IFDEF DARWIN}
2710 miApple.Enabled := True;
2711 miApple.Visible := True;
2712 miMacRecentSubMenu.Enabled := True;
2713 miMacRecentSubMenu.Visible := True;
2714 miWinRecentStart.Enabled := False;
2715 miWinRecentStart.Visible := False;
2716 miWinRecent.Enabled := False;
2717 miWinRecent.Visible := False;
2718 miLine2.Enabled := False;
2719 miLine2.Visible := False;
2720 miExit.Enabled := False;
2721 miExit.Visible := False;
2722 miOptions.Enabled := False;
2723 miOptions.Visible := False;
2724 miMenuWindow.Enabled := True;
2725 miMenuWindow.Visible := True;
2726 miAbout.Enabled := False;
2727 miAbout.Visible := False;
2728 {$ELSE}
2729 miApple.Enabled := False;
2730 miApple.Visible := False;
2731 miMacRecentSubMenu.Enabled := False;
2732 miMacRecentSubMenu.Visible := False;
2733 miWinRecentStart.Enabled := True;
2734 miWinRecentStart.Visible := True;
2735 miWinRecent.Enabled := True;
2736 miWinRecent.Visible := True;
2737 miLine2.Enabled := True;
2738 miLine2.Visible := True;
2739 miExit.Enabled := True;
2740 miExit.Visible := True;
2741 miOptions.Enabled := True;
2742 miOptions.Visible := True;
2743 miMenuWindow.Enabled := False;
2744 miMenuWindow.Visible := False;
2745 miAbout.Enabled := True;
2746 miAbout.Visible := True;
2747 {$ENDIF}
2749 miNewMap.ShortCut := ShortCut(VK_N, [ssModifier]);
2750 miOpenMap.ShortCut := ShortCut(VK_O, [ssModifier]);
2751 miSaveMap.ShortCut := ShortCut(VK_S, [ssModifier]);
2752 {$IFDEF DARWIN}
2753 miSaveMapAs.ShortCut := ShortCut(VK_S, [ssModifier, ssShift]);
2754 miReopenMap.ShortCut := ShortCut(VK_F5, [ssModifier]);
2755 {$ENDIF}
2756 miUndo.ShortCut := ShortCut(VK_Z, [ssModifier]);
2757 miCopy.ShortCut := ShortCut(VK_C, [ssModifier]);
2758 miCut.ShortCut := ShortCut(VK_X, [ssModifier]);
2759 miPaste.ShortCut := ShortCut(VK_V, [ssModifier]);
2760 miSelectAll.ShortCut := ShortCut(VK_A, [ssModifier]);
2761 miToFore.ShortCut := ShortCut(VK_LCL_CLOSE_BRACKET, [ssModifier]);
2762 miToBack.ShortCut := ShortCut(VK_LCL_OPEN_BRACKET, [ssModifier]);
2763 {$IFDEF DARWIN}
2764 miMapOptions.Shortcut := ShortCut(VK_P, [ssModifier, ssAlt]);
2765 selectall1.Shortcut := ShortCut(VK_A, [ssModifier, ssAlt]);
2766 {$ENDIF}
2768 e_WriteLog('Doom 2D: Forever Editor version ' + EDITOR_VERSION, MSG_NOTIFY);
2769 e_WriteLog('Build date: ' + EDITOR_BUILDDATE + ' ' + EDITOR_BUILDTIME, MSG_NOTIFY);
2770 e_WriteLog('Build hash: ' + g_GetBuildHash(), MSG_NOTIFY);
2771 e_WriteLog('Build by: ' + g_GetBuilderName(), MSG_NOTIFY);
2773 slInvalidTextures := TStringList.Create;
2775 ShowLayer(LAYER_BACK, True);
2776 ShowLayer(LAYER_WALLS, True);
2777 ShowLayer(LAYER_FOREGROUND, True);
2778 ShowLayer(LAYER_STEPS, True);
2779 ShowLayer(LAYER_WATER, True);
2780 ShowLayer(LAYER_ITEMS, True);
2781 ShowLayer(LAYER_MONSTERS, True);
2782 ShowLayer(LAYER_AREAS, True);
2783 ShowLayer(LAYER_TRIGGERS, True);
2785 ClearMap();
2787 FormCaption := MainForm.Caption;
2788 OpenedMap := '';
2789 OpenedWAD := '';
2791 config := TConfig.CreateFile(CfgFileName);
2793 if config.ReadInt('Editor', 'XPos', -1) = -1 then
2794 Position := poDesktopCenter
2795 else begin
2796 Left := config.ReadInt('Editor', 'XPos', Left);
2797 Top := config.ReadInt('Editor', 'YPos', Top);
2798 Width := config.ReadInt('Editor', 'Width', Width);
2799 Height := config.ReadInt('Editor', 'Height', Height);
2800 end;
2801 if config.ReadBool('Editor', 'Maximize', False) then
2802 WindowState := wsMaximized;
2803 ShowMap := config.ReadBool('Editor', 'Minimap', False);
2804 PanelProps.Width := config.ReadInt('Editor', 'PanelProps', PanelProps.ClientWidth);
2805 Splitter1.Left := PanelProps.Left;
2806 PanelObjs.Height := config.ReadInt('Editor', 'PanelObjs', PanelObjs.ClientHeight);
2807 Splitter2.Top := PanelObjs.Top;
2808 StatusBar.Top := PanelObjs.BoundsRect.Bottom;
2809 DotEnable := config.ReadBool('Editor', 'DotEnable', True);
2810 DotColor := config.ReadInt('Editor', 'DotColor', $FFFFFF);
2811 DotStepOne := config.ReadInt('Editor', 'DotStepOne', 16);
2812 DotStepTwo := config.ReadInt('Editor', 'DotStepTwo', 8);
2813 DotStep := config.ReadInt('Editor', 'DotStep', DotStepOne);
2814 DrawTexturePanel := config.ReadBool('Editor', 'DrawTexturePanel', True);
2815 DrawPanelSize := config.ReadBool('Editor', 'DrawPanelSize', True);
2816 BackColor := config.ReadInt('Editor', 'BackColor', $7F6040);
2817 PreviewColor := config.ReadInt('Editor', 'PreviewColor', $00FF00);
2818 UseCheckerboard := config.ReadBool('Editor', 'UseCheckerboard', True);
2819 gColorEdge := config.ReadInt('Editor', 'EdgeColor', COLOR_EDGE);
2820 gAlphaEdge := config.ReadInt('Editor', 'EdgeAlpha', ALPHA_EDGE);
2821 if gAlphaEdge = 255 then
2822 gAlphaEdge := ALPHA_EDGE;
2823 drEdge[0] := GetRValue(gColorEdge);
2824 drEdge[1] := GetGValue(gColorEdge);
2825 drEdge[2] := GetBValue(gColorEdge);
2826 if not config.ReadBool('Editor', 'EdgeShow', True) then
2827 drEdge[3] := 255
2828 else
2829 drEdge[3] := gAlphaEdge;
2830 gAlphaTriggerLine := config.ReadInt('Editor', 'LineAlpha', ALPHA_LINE);
2831 if gAlphaTriggerLine = 255 then
2832 gAlphaTriggerLine := ALPHA_LINE;
2833 gAlphaTriggerArea := config.ReadInt('Editor', 'TriggerAlpha', ALPHA_AREA);
2834 if gAlphaTriggerArea = 255 then
2835 gAlphaTriggerArea := ALPHA_AREA;
2836 gAlphaMonsterRect := config.ReadInt('Editor', 'MonsterRectAlpha', 0);
2837 gAlphaAreaRect := config.ReadInt('Editor', 'AreaRectAlpha', 0);
2838 if config.ReadInt('Editor', 'Scale', 0) = 1 then
2839 Scale := 2
2840 else
2841 Scale := 1;
2842 if config.ReadInt('Editor', 'DotSize', 0) = 1 then
2843 DotSize := 2
2844 else
2845 DotSize := 1;
2846 OpenDialog.InitialDir := config.ReadStr('Editor', 'LastOpenDir', MapsDir);
2847 SaveDialog.InitialDir := config.ReadStr('Editor', 'LastSaveDir', MapsDir);
2849 s := config.ReadStr('Editor', 'Language', '');
2850 gLanguage := s;
2852 Compress := config.ReadBool('Editor', 'Compress', True);
2853 Backup := config.ReadBool('Editor', 'Backup', True);
2855 TestGameMode := config.ReadStr('TestRun', 'GameMode', 'DM');
2856 TestLimTime := config.ReadStr('TestRun', 'LimTime', '0');
2857 TestLimScore := config.ReadStr('TestRun', 'LimScore', '0');
2858 TestOptionsTwoPlayers := config.ReadBool('TestRun', 'TwoPlayers', False);
2859 TestOptionsTeamDamage := config.ReadBool('TestRun', 'TeamDamage', False);
2860 TestOptionsAllowExit := config.ReadBool('TestRun', 'AllowExit', True);
2861 TestOptionsWeaponStay := config.ReadBool('TestRun', 'WeaponStay', False);
2862 TestOptionsMonstersDM := config.ReadBool('TestRun', 'MonstersDM', False);
2863 TestMapOnce := config.ReadBool('TestRun', 'MapOnce', False);
2864 {$IF DEFINED(DARWIN)}
2865 TestD2dExe := config.ReadStr('TestRun', 'ExeDrawin', GameExeFile);
2866 {$ELSEIF DEFINED(WINDOWS)}
2867 TestD2dExe := config.ReadStr('TestRun', 'ExeWindows', GameExeFile);
2868 {$ELSE}
2869 TestD2dExe := config.ReadStr('TestRun', 'ExeUnix', GameExeFile);
2870 {$ENDIF}
2871 TestD2DArgs := config.ReadStr('TestRun', 'Args', '');
2873 RecentCount := config.ReadInt('Editor', 'RecentCount', 5);
2874 if RecentCount > 10 then
2875 RecentCount := 10;
2876 if RecentCount < 2 then
2877 RecentCount := 2;
2879 RecentFiles := TStringList.Create();
2880 for i := 0 to RecentCount-1 do
2881 begin
2882 {$IFDEF WINDOWS}
2883 s := config.ReadStr('RecentFilesWin', IntToStr(i), '');
2884 {$ELSE}
2885 s := config.ReadStr('RecentFilesUnix', IntToStr(i), '');
2886 {$ENDIF}
2887 if s <> '' then
2888 RecentFiles.Add(s);
2889 end;
2890 RefreshRecentMenu();
2892 config.Free();
2894 tbShowMap.Down := ShowMap;
2895 tbGridOn.Down := DotEnable;
2896 pcObjects.ActivePageIndex := 0;
2897 Application.Title := MsgEditorTitle;
2899 Application.OnIdle := OnIdle;
2900 end;
2902 procedure PrintBlack(X, Y: Integer; Text: string; FontID: DWORD);
2903 begin
2904 // NOTE: all the font printing routines assume CP1251
2905 e_TextureFontPrintEx(X, Y, Text, FontID, 0, 0, 0, 1.0);
2906 end;
2908 procedure TMainForm.Draw();
2909 var
2910 x, y: Integer;
2911 a, b: Integer;
2912 ID, PID: DWORD;
2913 Width, Height: Word;
2914 Rect: TRectWH;
2915 ObjCount: Word;
2916 aX, aY, aX2, aY2, XX, ScaleSz: Integer;
2917 begin
2918 ID := 0;
2919 PID := 0;
2920 Width := 0;
2921 Height := 0;
2923 e_BeginRender();
2925 e_Clear(GL_COLOR_BUFFER_BIT,
2926 GetRValue(BackColor)/255,
2927 GetGValue(BackColor)/255,
2928 GetBValue(BackColor)/255);
2930 DrawMap();
2932 ObjCount := SelectedObjectCount();
2934 // Обводим выделенные объекты красной рамкой:
2935 if ObjCount > 0 then
2936 begin
2937 for a := 0 to High(SelectedObjects) do
2938 if SelectedObjects[a].Live then
2939 begin
2940 Rect := ObjectGetRect(SelectedObjects[a].ObjectType, SelectedObjects[a].ID);
2942 with Rect do
2943 begin
2944 e_DrawQuad(X+MapOffset.X, Y+MapOffset.Y,
2945 X+MapOffset.X+Width-1, Y+MapOffset.Y+Height-1,
2946 255, 0, 0);
2948 // Рисуем точки изменения размеров:
2949 if (ObjCount = 1) and
2950 (SelectedObjects[GetFirstSelected].ObjectType in [OBJECT_PANEL, OBJECT_TRIGGER]) then
2951 begin
2952 e_DrawPoint(5, X+MapOffset.X, Y+MapOffset.Y+(Height div 2), 255, 255, 255);
2953 e_DrawPoint(5, X+MapOffset.X+Width-1, Y+MapOffset.Y+(Height div 2), 255, 255, 255);
2954 e_DrawPoint(5, X+MapOffset.X+(Width div 2), Y+MapOffset.Y, 255, 255, 255);
2955 e_DrawPoint(5, X+MapOffset.X+(Width div 2), Y+MapOffset.Y+Height-1, 255, 255, 255);
2957 e_DrawPoint(3, X+MapOffset.X, Y+MapOffset.Y+(Height div 2), 255, 0, 0);
2958 e_DrawPoint(3, X+MapOffset.X+Width-1, Y+MapOffset.Y+(Height div 2), 255, 0, 0);
2959 e_DrawPoint(3, X+MapOffset.X+(Width div 2), Y+MapOffset.Y, 255, 0, 0);
2960 e_DrawPoint(3, X+MapOffset.X+(Width div 2), Y+MapOffset.Y+Height-1, 255, 0, 0);
2961 end;
2962 end;
2963 end;
2964 end;
2966 // Рисуем сетку:
2967 if DotEnable and (PreviewMode = 0) then
2968 begin
2969 if DotSize = 2 then
2970 a := -1
2971 else
2972 a := 0;
2974 x := MapOffset.X mod DotStep;
2975 y := MapOffset.Y mod DotStep;
2977 while x < RenderPanel.Width do
2978 begin
2979 while y < RenderPanel.Height do
2980 begin
2981 e_DrawPoint(DotSize, x + a, y + a,
2982 GetRValue(DotColor),
2983 GetGValue(DotColor),
2984 GetBValue(DotColor));
2985 y += DotStep;
2986 end;
2987 x += DotStep;
2988 y := MapOffset.Y mod DotStep;
2989 end;
2990 end;
2992 // Превью текстуры:
2993 if (lbTextureList.ItemIndex <> -1) and (cbPreview.Checked) and
2994 (not IsSpecialTextureSel()) and (PreviewMode = 0) then
2995 begin
2996 if not g_GetTexture(SelectedTexture(), ID) then
2997 g_GetTexture('NOTEXTURE', ID);
2998 g_GetTextureSizeByID(ID, Width, Height);
2999 if UseCheckerboard then
3000 begin
3001 if g_GetTexture('PREVIEW', PID) then
3002 e_DrawFill(PID, RenderPanel.Width-Width, RenderPanel.Height-Height, Width div 16 + 1, Height div 16 + 1, 0, True, False);
3003 end else
3004 e_DrawFillQuad(RenderPanel.Width-Width-2, RenderPanel.Height-Height-2,
3005 RenderPanel.Width-1, RenderPanel.Height-1,
3006 GetRValue(PreviewColor), GetGValue(PreviewColor), GetBValue(PreviewColor), 0);
3007 e_Draw(ID, RenderPanel.Width-Width, RenderPanel.Height-Height, 0, True, False);
3008 end;
3010 // Подсказка при выборе точки Телепорта:
3011 if SelectFlag = SELECTFLAG_TELEPORT then
3012 begin
3013 with gTriggers[SelectedObjects[GetFirstSelected()].ID] do
3014 if Data.d2d_teleport then
3015 e_DrawLine(2, MousePos.X-16, MousePos.Y-1,
3016 MousePos.X+16, MousePos.Y-1,
3017 0, 0, 255)
3018 else
3019 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+AreaSize[AREA_DMPOINT].Width-1,
3020 MousePos.Y+AreaSize[AREA_DMPOINT].Height-1, 255, 255, 255);
3022 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 192, 192, 192, 127);
3023 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 255, 255, 255);
3024 PrintBlack(MousePos.X+2, MousePos.Y+2, utf8to1251(MsgHintTeleport), gEditorFont);
3025 end;
3027 // Подсказка при выборе точки появления:
3028 if SelectFlag = SELECTFLAG_SPAWNPOINT then
3029 begin
3030 e_DrawLine(2, MousePos.X-16, MousePos.Y-1,
3031 MousePos.X+16, MousePos.Y-1,
3032 0, 0, 255);
3033 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 192, 192, 192, 127);
3034 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 255, 255, 255);
3035 PrintBlack(MousePos.X+2, MousePos.Y+2, utf8to1251(MsgHintSpawn), gEditorFont);
3036 end;
3038 // Подсказка при выборе панели двери:
3039 if SelectFlag = SELECTFLAG_DOOR then
3040 begin
3041 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 192, 192, 192, 127);
3042 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 255, 255, 255);
3043 PrintBlack(MousePos.X+2, MousePos.Y+2, utf8to1251(MsgHintPanelDoor), gEditorFont);
3044 end;
3046 // Подсказка при выборе панели с текстурой:
3047 if SelectFlag = SELECTFLAG_TEXTURE then
3048 begin
3049 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+196, MousePos.Y+18, 192, 192, 192, 127);
3050 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+196, MousePos.Y+18, 255, 255, 255);
3051 PrintBlack(MousePos.X+2, MousePos.Y+2, utf8to1251(MsgHintPanelTexture), gEditorFont);
3052 end;
3054 // Подсказка при выборе панели индикации выстрела:
3055 if SelectFlag = SELECTFLAG_SHOTPANEL then
3056 begin
3057 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+316, MousePos.Y+18, 192, 192, 192, 127);
3058 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+316, MousePos.Y+18, 255, 255, 255);
3059 PrintBlack(MousePos.X+2, MousePos.Y+2, utf8to1251(MsgHintPanelShot), gEditorFont);
3060 end;
3062 // Подсказка при выборе панели лифта:
3063 if SelectFlag = SELECTFLAG_LIFT then
3064 begin
3065 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 192, 192, 192, 127);
3066 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+180, MousePos.Y+18, 255, 255, 255);
3067 PrintBlack(MousePos.X+2, MousePos.Y+2, utf8to1251(MsgHintPanelLift), gEditorFont);
3068 end;
3070 // Подсказка при выборе монстра:
3071 if SelectFlag = SELECTFLAG_MONSTER then
3072 begin
3073 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+120, MousePos.Y+18, 192, 192, 192, 127);
3074 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+120, MousePos.Y+18, 255, 255, 255);
3075 PrintBlack(MousePos.X+2, MousePos.Y+2, utf8to1251(MsgHintMonster), gEditorFont);
3076 end;
3078 // Подсказка при выборе области воздействия:
3079 if DrawPressRect then
3080 begin
3081 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+204, MousePos.Y+18, 192, 192, 192, 127);
3082 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+204, MousePos.Y+18, 255, 255, 255);
3083 PrintBlack(MousePos.X+2, MousePos.Y+2, utf8to1251(MsgHintExtArea), gEditorFont);
3084 end;
3086 // Рисуем текстуры, если чертим панель:
3087 if (MouseAction = MOUSEACTION_DRAWPANEL) and (DrawTexturePanel) and
3088 (lbTextureList.ItemIndex <> -1) and (DrawRect <> nil) and
3089 (lbPanelType.ItemIndex in [0..8]) and not IsSpecialTextureSel() then
3090 begin
3091 if not g_GetTexture(SelectedTexture(), ID) then
3092 g_GetTexture('NOTEXTURE', ID);
3093 g_GetTextureSizeByID(ID, Width, Height);
3094 with DrawRect^ do
3095 if (Abs(Right-Left) >= Width) and (Abs(Bottom-Top) >= Height) then
3096 e_DrawFill(ID, Min(Left, Right), Min(Top, Bottom), Abs(Right-Left) div Width,
3097 Abs(Bottom-Top) div Height, 64, True, False);
3098 end;
3100 // Прямоугольник выделения:
3101 if DrawRect <> nil then
3102 with DrawRect^ do
3103 e_DrawQuad(Left, Top, Right-1, Bottom-1, 255, 255, 255);
3105 // Чертим мышью панель/триггер или меняем мышью их размер:
3106 if (((MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER]) and
3107 not(ssCtrl in GetKeyShiftState())) or (MouseAction = MOUSEACTION_RESIZE)) and
3108 (DrawPanelSize) then
3109 begin
3110 e_DrawFillQuad(MousePos.X, MousePos.Y, MousePos.X+88, MousePos.Y+33, 192, 192, 192, 127);
3111 e_DrawQuad(MousePos.X, MousePos.Y, MousePos.X+88, MousePos.Y+33, 255, 255, 255);
3113 if MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER] then
3114 begin // Чертим новый
3115 PrintBlack(MousePos.X+2, MousePos.Y+2, Format(utf8to1251(MsgHintWidth),
3116 [Abs(MousePos.X-MouseLDownPos.X)]), gEditorFont);
3117 PrintBlack(MousePos.X+2, MousePos.Y+16, Format(utf8to1251(MsgHintHeight),
3118 [Abs(MousePos.Y-MouseLDownPos.Y)]), gEditorFont);
3119 end
3120 else // Растягиваем существующий
3121 if SelectedObjects[GetFirstSelected].ObjectType in [OBJECT_PANEL, OBJECT_TRIGGER] then
3122 begin
3123 if SelectedObjects[GetFirstSelected].ObjectType = OBJECT_PANEL then
3124 begin
3125 Width := gPanels[SelectedObjects[GetFirstSelected].ID].Width;
3126 Height := gPanels[SelectedObjects[GetFirstSelected].ID].Height;
3127 end
3128 else
3129 begin
3130 Width := gTriggers[SelectedObjects[GetFirstSelected].ID].Width;
3131 Height := gTriggers[SelectedObjects[GetFirstSelected].ID].Height;
3132 end;
3134 PrintBlack(MousePos.X+2, MousePos.Y+2, Format(utf8to1251(MsgHintWidth), [Width]),
3135 gEditorFont);
3136 PrintBlack(MousePos.X+2, MousePos.Y+16, Format(utf8to1251(MsgHintHeight), [Height]),
3137 gEditorFont);
3138 end;
3139 end;
3141 // Ближайшая к курсору мыши точка на сетке:
3142 e_DrawPoint(3, MousePos.X, MousePos.Y, 0, 0, 255);
3144 // Мини-карта:
3145 if ShowMap then
3146 begin
3147 // Сколько пикселов карты в 1 пикселе мини-карты:
3148 ScaleSz := 16 div Scale;
3149 // Размеры мини-карты:
3150 aX := max(gMapInfo.Width div ScaleSz, 1);
3151 aY := max(gMapInfo.Height div ScaleSz, 1);
3152 // X-координата на RenderPanel нулевой x-координаты карты:
3153 XX := RenderPanel.Width - aX - 1;
3154 // Рамка карты:
3155 e_DrawFillQuad(XX-1, 0, RenderPanel.Width-1, aY+1, 0, 0, 0, 0);
3156 e_DrawQuad(XX-1, 0, RenderPanel.Width-1, aY+1, 197, 197, 197);
3158 if gPanels <> nil then
3159 begin
3160 // Рисуем панели:
3161 for a := 0 to High(gPanels) do
3162 with gPanels[a] do
3163 if PanelType <> 0 then
3164 begin
3165 // Левый верхний угол:
3166 aX := XX + (X div ScaleSz);
3167 aY := 1 + (Y div ScaleSz);
3168 // Размеры:
3169 aX2 := max(Width div ScaleSz, 1);
3170 aY2 := max(Height div ScaleSz, 1);
3171 // Правый нижний угол:
3172 aX2 := aX + aX2 - 1;
3173 aY2 := aY + aY2 - 1;
3175 case PanelType of
3176 PANEL_WALL: e_DrawFillQuad(aX, aY, aX2, aY2, 208, 208, 208, 0);
3177 PANEL_WATER: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 0, 192, 0);
3178 PANEL_ACID1: e_DrawFillQuad(aX, aY, aX2, aY2, 0, 176, 0, 0);
3179 PANEL_ACID2: e_DrawFillQuad(aX, aY, aX2, aY2, 176, 0, 0, 0);
3180 PANEL_STEP: e_DrawFillQuad(aX, aY, aX2, aY2, 128, 128, 128, 0);
3181 PANEL_LIFTUP: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 72, 36, 0);
3182 PANEL_LIFTDOWN: e_DrawFillQuad(aX, aY, aX2, aY2, 116, 124, 96, 0);
3183 PANEL_LIFTLEFT: e_DrawFillQuad(aX, aY, aX2, aY2, 200, 80, 4, 0);
3184 PANEL_LIFTRIGHT: e_DrawFillQuad(aX, aY, aX2, aY2, 252, 140, 56, 0);
3185 PANEL_OPENDOOR: e_DrawFillQuad(aX, aY, aX2, aY2, 100, 220, 92, 0);
3186 PANEL_CLOSEDOOR: e_DrawFillQuad(aX, aY, aX2, aY2, 212, 184, 64, 0);
3187 PANEL_BLOCKMON: e_DrawFillQuad(aX, aY, aX2, aY2, 192, 0, 192, 0);
3188 end;
3189 end;
3191 // Рисуем красным выделенные панели:
3192 if SelectedObjects <> nil then
3193 for b := 0 to High(SelectedObjects) do
3194 with SelectedObjects[b] do
3195 if Live and (ObjectType = OBJECT_PANEL) then
3196 with gPanels[SelectedObjects[b].ID] do
3197 if PanelType and not(PANEL_BACK or PANEL_FORE) <> 0 then
3198 begin
3199 // Левый верхний угол:
3200 aX := XX + (X div ScaleSz);
3201 aY := 1 + (Y div ScaleSz);
3202 // Размеры:
3203 aX2 := max(Width div ScaleSz, 1);
3204 aY2 := max(Height div ScaleSz, 1);
3205 // Правый нижний угол:
3206 aX2 := aX + aX2 - 1;
3207 aY2 := aY + aY2 - 1;
3209 e_DrawFillQuad(aX, aY, aX2, aY2, 255, 0, 0, 0)
3210 end;
3211 end;
3213 if (gMapInfo.Width > RenderPanel.Width) or
3214 (gMapInfo.Height > RenderPanel.Height) then
3215 begin
3216 // Окно, показывающее текущее положение экрана на карте:
3217 // Размеры окна:
3218 x := max(min(RenderPanel.Width, gMapInfo.Width) div ScaleSz, 1);
3219 y := max(min(RenderPanel.Height, gMapInfo.Height) div ScaleSz, 1);
3220 // Левый верхний угол:
3221 aX := XX + ((-MapOffset.X) div ScaleSz);
3222 aY := 1 + ((-MapOffset.Y) div ScaleSz);
3223 // Правый нижний угол:
3224 aX2 := aX + x - 1;
3225 aY2 := aY + y - 1;
3227 e_DrawFillQuad(aX, aY, aX2, aY2, 127, 192, 127, 127, B_BLEND);
3228 e_DrawQuad(aX, aY, aX2, aY2, 255, 0, 0);
3229 end;
3230 end; // Мини-карта
3232 e_EndRender();
3233 RenderPanel.SwapBuffers();
3234 end;
3236 procedure TMainForm.FormResize(Sender: TObject);
3237 begin
3238 e_SetViewPort(0, 0, RenderPanel.Width, RenderPanel.Height);
3240 sbHorizontal.Min := Min(gMapInfo.Width - RenderPanel.Width, -RenderPanel.Width div 2);
3241 sbHorizontal.Max := Max(0, gMapInfo.Width - RenderPanel.Width div 2);
3242 sbVertical.Min := Min(gMapInfo.Height - RenderPanel.Height, -RenderPanel.Height div 2);
3243 sbVertical.Max := Max(0, gMapInfo.Height - RenderPanel.Height div 2);
3245 MapOffset.X := -sbHorizontal.Position;
3246 MapOffset.Y := -sbVertical.Position;
3247 end;
3249 procedure TMainForm.FormWindowStateChange(Sender: TObject);
3250 {$IFDEF DARWIN}
3251 var e: Boolean;
3252 {$ENDIF}
3253 begin
3254 {$IFDEF DARWIN}
3255 // deactivate all menus when main window minimized
3256 e := self.WindowState <> wsMinimized;
3257 miMenuFile.Enabled := e;
3258 miMenuEdit.Enabled := e;
3259 miMenuView.Enabled := e;
3260 miMenuService.Enabled := e;
3261 miMenuWindow.Enabled := e;
3262 miMenuHelp.Enabled := e;
3263 miMenuHidden.Enabled := e;
3264 {$ENDIF}
3265 end;
3267 procedure SelectNextObject(X, Y: Integer; ObjectType: Byte; ID: DWORD);
3268 var
3269 j, j_max: Integer;
3270 res: Boolean;
3271 begin
3272 j_max := 0; // shut up compiler
3273 case ObjectType of
3274 OBJECT_PANEL:
3275 begin
3276 res := (gPanels <> nil) and
3277 PanelInShownLayer(gPanels[ID].PanelType) and
3278 g_CollidePoint(X, Y, gPanels[ID].X, gPanels[ID].Y,
3279 gPanels[ID].Width,
3280 gPanels[ID].Height);
3281 j_max := Length(gPanels) - 1;
3282 end;
3284 OBJECT_ITEM:
3285 begin
3286 res := (gItems <> nil) and
3287 LayerEnabled[LAYER_ITEMS] and
3288 g_CollidePoint(X, Y, gItems[ID].X, gItems[ID].Y,
3289 ItemSize[gItems[ID].ItemType][0],
3290 ItemSize[gItems[ID].ItemType][1]);
3291 j_max := Length(gItems) - 1;
3292 end;
3294 OBJECT_MONSTER:
3295 begin
3296 res := (gMonsters <> nil) and
3297 LayerEnabled[LAYER_MONSTERS] and
3298 g_CollidePoint(X, Y, gMonsters[ID].X, gMonsters[ID].Y,
3299 MonsterSize[gMonsters[ID].MonsterType].Width,
3300 MonsterSize[gMonsters[ID].MonsterType].Height);
3301 j_max := Length(gMonsters) - 1;
3302 end;
3304 OBJECT_AREA:
3305 begin
3306 res := (gAreas <> nil) and
3307 LayerEnabled[LAYER_AREAS] and
3308 g_CollidePoint(X, Y, gAreas[ID].X, gAreas[ID].Y,
3309 AreaSize[gAreas[ID].AreaType].Width,
3310 AreaSize[gAreas[ID].AreaType].Height);
3311 j_max := Length(gAreas) - 1;
3312 end;
3314 OBJECT_TRIGGER:
3315 begin
3316 res := (gTriggers <> nil) and
3317 LayerEnabled[LAYER_TRIGGERS] and
3318 g_CollidePoint(X, Y, gTriggers[ID].X, gTriggers[ID].Y,
3319 gTriggers[ID].Width,
3320 gTriggers[ID].Height);
3321 j_max := Length(gTriggers) - 1;
3322 end;
3324 else
3325 res := False;
3326 end;
3328 if not res then
3329 Exit;
3331 // Перебор ID: от ID-1 до 0; потом от High до ID+1:
3332 j := ID;
3334 while True do
3335 begin
3336 Dec(j);
3338 if j < 0 then
3339 j := j_max;
3340 if j = Integer(ID) then
3341 Break;
3343 case ObjectType of
3344 OBJECT_PANEL:
3345 res := PanelInShownLayer(gPanels[j].PanelType) and
3346 g_CollidePoint(X, Y, gPanels[j].X, gPanels[j].Y,
3347 gPanels[j].Width,
3348 gPanels[j].Height);
3349 OBJECT_ITEM:
3350 res := (gItems[j].ItemType <> ITEM_NONE) and
3351 g_CollidePoint(X, Y, gItems[j].X, gItems[j].Y,
3352 ItemSize[gItems[j].ItemType][0],
3353 ItemSize[gItems[j].ItemType][1]);
3354 OBJECT_MONSTER:
3355 res := (gMonsters[j].MonsterType <> MONSTER_NONE) and
3356 g_CollidePoint(X, Y, gMonsters[j].X, gMonsters[j].Y,
3357 MonsterSize[gMonsters[j].MonsterType].Width,
3358 MonsterSize[gMonsters[j].MonsterType].Height);
3359 OBJECT_AREA:
3360 res := (gAreas[j].AreaType <> AREA_NONE) and
3361 g_CollidePoint(X, Y, gAreas[j].X, gAreas[j].Y,
3362 AreaSize[gAreas[j].AreaType].Width,
3363 AreaSize[gAreas[j].AreaType].Height);
3364 OBJECT_TRIGGER:
3365 res := (gTriggers[j].TriggerType <> TRIGGER_NONE) and
3366 g_CollidePoint(X, Y, gTriggers[j].X, gTriggers[j].Y,
3367 gTriggers[j].Width,
3368 gTriggers[j].Height);
3369 else
3370 res := False;
3371 end;
3373 if res then
3374 begin
3375 SetLength(SelectedObjects, 1);
3377 SelectedObjects[0].ObjectType := ObjectType;
3378 SelectedObjects[0].ID := j;
3379 SelectedObjects[0].Live := True;
3381 FillProperty();
3382 Break;
3383 end;
3384 end;
3385 end;
3387 procedure TMainForm.RenderPanelMouseDown(Sender: TObject;
3388 Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
3389 var
3390 i: Integer;
3391 Rect: TRectWH;
3392 c1, c2, c3, c4: Boolean;
3393 item: TItem;
3394 area: TArea;
3395 monster: TMonster;
3396 IDArray: DWArray;
3397 begin
3398 MainForm.ActiveControl := RenderPanel;
3399 RenderPanel.SetFocus();
3401 RenderPanelMouseMove(RenderPanel, Shift, X, Y);
3403 if Button = mbLeft then // Left Mouse Button
3404 begin
3405 // Двигаем карту с помощью мыши и мини-карты:
3406 if ShowMap and
3407 g_CollidePoint(X, Y,
3408 RenderPanel.Width-max(gMapInfo.Width div (16 div Scale), 1)-1,
3409 1,
3410 max(gMapInfo.Width div (16 div Scale), 1),
3411 max(gMapInfo.Height div (16 div Scale), 1) ) then
3412 begin
3413 MoveMap(X, Y);
3414 MouseAction := MOUSEACTION_MOVEMAP;
3415 end
3416 else // Ставим предмет/монстра/область:
3417 if (pcObjects.ActivePageIndex in [1, 2, 3]) and
3418 (not (ssShift in Shift)) then
3419 begin
3420 case pcObjects.ActivePageIndex of
3421 1:
3422 if lbItemList.ItemIndex = -1 then
3423 ErrorMessageBox(MsgMsgChooseItem)
3424 else
3425 begin
3426 item.ItemType := lbItemList.ItemIndex + ITEM_MEDKIT_SMALL;
3427 if item.ItemType >= ITEM_WEAPON_KASTET then
3428 item.ItemType := item.ItemType + 2;
3429 item.X := MousePos.X-MapOffset.X;
3430 item.Y := MousePos.Y-MapOffset.Y;
3432 if not (ssCtrl in Shift) then
3433 begin
3434 item.X := item.X - (ItemSize[item.ItemType][0] div 2);
3435 item.Y := item.Y - ItemSize[item.ItemType][1];
3436 end;
3438 item.OnlyDM := cbOnlyDM.Checked;
3439 item.Fall := cbFall.Checked;
3440 Undo_Add(OBJECT_ITEM, AddItem(item));
3441 end;
3442 2:
3443 if lbMonsterList.ItemIndex = -1 then
3444 ErrorMessageBox(MsgMsgChooseMonster)
3445 else
3446 begin
3447 monster.MonsterType := lbMonsterList.ItemIndex + MONSTER_DEMON;
3448 monster.X := MousePos.X-MapOffset.X;
3449 monster.Y := MousePos.Y-MapOffset.Y;
3451 if not (ssCtrl in Shift) then
3452 begin
3453 monster.X := monster.X - (MonsterSize[monster.MonsterType].Width div 2);
3454 monster.Y := monster.Y - MonsterSize[monster.MonsterType].Height;
3455 end;
3457 if rbMonsterLeft.Checked then
3458 monster.Direction := D_LEFT
3459 else
3460 monster.Direction := D_RIGHT;
3461 Undo_Add(OBJECT_MONSTER, AddMonster(monster));
3462 end;
3463 3:
3464 if lbAreasList.ItemIndex = -1 then
3465 ErrorMessageBox(MsgMsgChooseArea)
3466 else
3467 if (lbAreasList.ItemIndex + 1) <> AREA_DOMFLAG then
3468 begin
3469 area.AreaType := lbAreasList.ItemIndex + AREA_PLAYERPOINT1;
3470 area.X := MousePos.X-MapOffset.X;
3471 area.Y := MousePos.Y-MapOffset.Y;
3473 if not (ssCtrl in Shift) then
3474 begin
3475 area.X := area.X - (AreaSize[area.AreaType].Width div 2);
3476 area.Y := area.Y - AreaSize[area.AreaType].Height;
3477 end;
3479 if rbAreaLeft.Checked then
3480 area.Direction := D_LEFT
3481 else
3482 area.Direction := D_RIGHT;
3483 Undo_Add(OBJECT_AREA, AddArea(area));
3484 end;
3485 end;
3486 end
3487 else
3488 begin
3489 i := GetFirstSelected();
3491 // Выбираем объект под текущим:
3492 if (SelectedObjects <> nil) and
3493 (ssShift in Shift) and (i >= 0) and
3494 (SelectedObjects[i].Live) then
3495 begin
3496 if SelectedObjectCount() = 1 then
3497 SelectNextObject(X-MapOffset.X, Y-MapOffset.Y,
3498 SelectedObjects[i].ObjectType,
3499 SelectedObjects[i].ID);
3500 end
3501 else
3502 begin
3503 // Рисуем область триггера "Расширитель":
3504 if DrawPressRect and (i >= 0) and
3505 (SelectedObjects[i].ObjectType = OBJECT_TRIGGER) and
3506 (gTriggers[SelectedObjects[i].ID].TriggerType in
3507 [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF]) then
3508 MouseAction := MOUSEACTION_DRAWPRESS
3509 else // Рисуем панель:
3510 if pcObjects.ActivePageIndex = 0 then
3511 begin
3512 if (lbPanelType.ItemIndex >= 0) then
3513 MouseAction := MOUSEACTION_DRAWPANEL
3514 end
3515 else // Рисуем триггер:
3516 if (lbTriggersList.ItemIndex >= 0) then
3517 begin
3518 MouseAction := MOUSEACTION_DRAWTRIGGER;
3519 end;
3520 end;
3521 end;
3522 end; // if Button = mbLeft
3524 if Button = mbRight then // Right Mouse Button
3525 begin
3526 // Клик по мини-карте:
3527 if ShowMap and
3528 g_CollidePoint(X, Y,
3529 RenderPanel.Width-max(gMapInfo.Width div (16 div Scale), 1)-1,
3530 1,
3531 max(gMapInfo.Width div (16 div Scale), 1),
3532 max(gMapInfo.Height div (16 div Scale), 1) ) then
3533 begin
3534 MouseAction := MOUSEACTION_NOACTION;
3535 end
3536 else // Нужно что-то выбрать мышью:
3537 if SelectFlag <> SELECTFLAG_NONE then
3538 begin
3539 case SelectFlag of
3540 SELECTFLAG_TELEPORT:
3541 // Точку назначения телепортации:
3542 with gTriggers[SelectedObjects[
3543 GetFirstSelected() ].ID].Data.TargetPoint do
3544 begin
3545 X := MousePos.X-MapOffset.X;
3546 Y := MousePos.Y-MapOffset.Y;
3547 end;
3549 SELECTFLAG_SPAWNPOINT:
3550 // Точку создания монстра:
3551 with gTriggers[SelectedObjects[GetFirstSelected()].ID] do
3552 if TriggerType = TRIGGER_SPAWNMONSTER then
3553 begin
3554 Data.MonPos.X := MousePos.X-MapOffset.X;
3555 Data.MonPos.Y := MousePos.Y-MapOffset.Y;
3556 end
3557 else if TriggerType = TRIGGER_SPAWNITEM then
3558 begin // Точка создания предмета:
3559 Data.ItemPos.X := MousePos.X-MapOffset.X;
3560 Data.ItemPos.Y := MousePos.Y-MapOffset.Y;
3561 end
3562 else if TriggerType = TRIGGER_SHOT then
3563 begin // Точка создания выстрела:
3564 Data.ShotPos.X := MousePos.X-MapOffset.X;
3565 Data.ShotPos.Y := MousePos.Y-MapOffset.Y;
3566 end;
3568 SELECTFLAG_DOOR:
3569 // Дверь:
3570 begin
3571 IDArray := ObjectInRect(X-MapOffset.X,
3572 Y-MapOffset.Y,
3573 2, 2, OBJECT_PANEL, True);
3574 if IDArray <> nil then
3575 begin
3576 for i := 0 to High(IDArray) do
3577 if (gPanels[IDArray[i]].PanelType = PANEL_OPENDOOR) or
3578 (gPanels[IDArray[i]].PanelType = PANEL_CLOSEDOOR) then
3579 begin
3580 gTriggers[SelectedObjects[
3581 GetFirstSelected() ].ID].Data.PanelID := IDArray[i];
3582 Break;
3583 end;
3584 end
3585 else
3586 gTriggers[SelectedObjects[
3587 GetFirstSelected() ].ID].Data.PanelID := -1;
3588 end;
3590 SELECTFLAG_TEXTURE:
3591 // Панель с текстурой:
3592 begin
3593 IDArray := ObjectInRect(X-MapOffset.X,
3594 Y-MapOffset.Y,
3595 2, 2, OBJECT_PANEL, True);
3596 if IDArray <> nil then
3597 begin
3598 for i := 0 to High(IDArray) do
3599 if ((gPanels[IDArray[i]].PanelType in
3600 [PANEL_WALL, PANEL_BACK, PANEL_FORE,
3601 PANEL_WATER, PANEL_ACID1, PANEL_ACID2,
3602 PANEL_STEP]) or
3603 (gPanels[IDArray[i]].PanelType = PANEL_OPENDOOR) or
3604 (gPanels[IDArray[i]].PanelType = PANEL_CLOSEDOOR)) and
3605 (gPanels[IDArray[i]].TextureName <> '') then
3606 begin
3607 gTriggers[SelectedObjects[
3608 GetFirstSelected() ].ID].TexturePanel := IDArray[i];
3609 Break;
3610 end;
3611 end
3612 else
3613 gTriggers[SelectedObjects[
3614 GetFirstSelected() ].ID].TexturePanel := -1;
3615 end;
3617 SELECTFLAG_LIFT:
3618 // Лифт:
3619 begin
3620 IDArray := ObjectInRect(X-MapOffset.X,
3621 Y-MapOffset.Y,
3622 2, 2, OBJECT_PANEL, True);
3623 if IDArray <> nil then
3624 begin
3625 for i := 0 to High(IDArray) do
3626 if (gPanels[IDArray[i]].PanelType = PANEL_LIFTUP) or
3627 (gPanels[IDArray[i]].PanelType = PANEL_LIFTDOWN) or
3628 (gPanels[IDArray[i]].PanelType = PANEL_LIFTLEFT) or
3629 (gPanels[IDArray[i]].PanelType = PANEL_LIFTRIGHT) then
3630 begin
3631 gTriggers[SelectedObjects[
3632 GetFirstSelected() ].ID].Data.PanelID := IDArray[i];
3633 Break;
3634 end;
3635 end
3636 else
3637 gTriggers[SelectedObjects[
3638 GetFirstSelected() ].ID].Data.PanelID := -1;
3639 end;
3641 SELECTFLAG_MONSTER:
3642 // Монстра:
3643 begin
3644 IDArray := ObjectInRect(X-MapOffset.X,
3645 Y-MapOffset.Y,
3646 2, 2, OBJECT_MONSTER, False);
3647 if IDArray <> nil then
3648 gTriggers[SelectedObjects[
3649 GetFirstSelected() ].ID].Data.MonsterID := IDArray[0]+1
3650 else
3651 gTriggers[SelectedObjects[
3652 GetFirstSelected() ].ID].Data.MonsterID := 0;
3653 end;
3655 SELECTFLAG_SHOTPANEL:
3656 // Панель индикации выстрела:
3657 begin
3658 if gTriggers[SelectedObjects[
3659 GetFirstSelected() ].ID].TriggerType = TRIGGER_SHOT then
3660 begin
3661 IDArray := ObjectInRect(X-MapOffset.X,
3662 Y-MapOffset.Y,
3663 2, 2, OBJECT_PANEL, True);
3664 if IDArray <> nil then
3665 begin
3666 for i := 0 to High(IDArray) do
3667 if ((gPanels[IDArray[i]].PanelType in
3668 [PANEL_WALL, PANEL_BACK, PANEL_FORE,
3669 PANEL_WATER, PANEL_ACID1, PANEL_ACID2,
3670 PANEL_STEP]) or
3671 (gPanels[IDArray[i]].PanelType = PANEL_OPENDOOR) or
3672 (gPanels[IDArray[i]].PanelType = PANEL_CLOSEDOOR)) and
3673 (gPanels[IDArray[i]].TextureName <> '') then
3674 begin
3675 gTriggers[SelectedObjects[
3676 GetFirstSelected() ].ID].Data.ShotPanelID := IDArray[i];
3677 Break;
3678 end;
3679 end
3680 else
3681 gTriggers[SelectedObjects[
3682 GetFirstSelected() ].ID].Data.ShotPanelID := -1;
3683 end;
3684 end;
3685 end;
3687 SelectFlag := SELECTFLAG_SELECTED;
3688 end
3689 else // if SelectFlag <> SELECTFLAG_NONE...
3690 begin
3691 // Что уже выбрано и не нажат Ctrl:
3692 if (SelectedObjects <> nil) and
3693 (not (ssCtrl in Shift)) then
3694 for i := 0 to High(SelectedObjects) do
3695 with SelectedObjects[i] do
3696 if Live then
3697 begin
3698 if (ObjectType in [OBJECT_PANEL, OBJECT_TRIGGER]) and
3699 (SelectedObjectCount() = 1) then
3700 begin
3701 Rect := ObjectGetRect(ObjectType, ID);
3703 c1 := g_Collide(X-MapOffset.X-1, Y-MapOffset.Y-1, 2, 2,
3704 Rect.X-2, Rect.Y+(Rect.Height div 2)-2, 4, 4);
3705 c2 := g_Collide(X-MapOffset.X-1, Y-MapOffset.Y-1, 2, 2,
3706 Rect.X+Rect.Width-3, Rect.Y+(Rect.Height div 2)-2, 4, 4);
3707 c3 := g_Collide(X-MapOffset.X-1, Y-MapOffset.Y-1, 2, 2,
3708 Rect.X+(Rect.Width div 2)-2, Rect.Y-2, 4, 4);
3709 c4 := g_Collide(X-MapOffset.X-1, Y-MapOffset.Y-1, 2, 2,
3710 Rect.X+(Rect.Width div 2)-2, Rect.Y+Rect.Height-3, 4, 4);
3712 // Меняем размер панели или триггера:
3713 if c1 or c2 or c3 or c4 then
3714 begin
3715 MouseAction := MOUSEACTION_RESIZE;
3716 LastMovePoint := MousePos;
3718 if c1 or c2 then
3719 begin // Шире/уже
3720 ResizeType := RESIZETYPE_HORIZONTAL;
3721 if c1 then
3722 ResizeDirection := RESIZEDIR_LEFT
3723 else
3724 ResizeDirection := RESIZEDIR_RIGHT;
3725 RenderPanel.Cursor := crSizeWE;
3726 end
3727 else
3728 begin // Выше/ниже
3729 ResizeType := RESIZETYPE_VERTICAL;
3730 if c3 then
3731 ResizeDirection := RESIZEDIR_UP
3732 else
3733 ResizeDirection := RESIZEDIR_DOWN;
3734 RenderPanel.Cursor := crSizeNS;
3735 end;
3737 Break;
3738 end;
3739 end;
3741 // Перемещаем панель или триггер:
3742 if ObjectCollide(ObjectType, ID,
3743 X-MapOffset.X-1,
3744 Y-MapOffset.Y-1, 2, 2) then
3745 begin
3746 MouseAction := MOUSEACTION_MOVEOBJ;
3747 LastMovePoint := MousePos;
3749 Break;
3750 end;
3751 end;
3752 end;
3753 end; // if Button = mbRight
3755 if Button = mbMiddle then // Middle Mouse Button
3756 begin
3757 SetCapture(RenderPanel.Handle);
3758 RenderPanel.Cursor := crSize;
3759 end;
3761 MouseMDown := Button = mbMiddle;
3762 if MouseMDown then
3763 MouseMDownPos := Mouse.CursorPos;
3765 MouseRDown := Button = mbRight;
3766 if MouseRDown then
3767 MouseRDownPos := MousePos;
3769 MouseLDown := Button = mbLeft;
3770 if MouseLDown then
3771 MouseLDownPos := MousePos;
3772 end;
3774 procedure TMainForm.RenderPanelMouseUp(Sender: TObject;
3775 Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
3776 var
3777 panel: TPanel;
3778 trigger: TTrigger;
3779 rRect: TRectWH;
3780 rSelectRect: Boolean;
3781 wWidth, wHeight: Word;
3782 TextureID: DWORD;
3784 procedure SelectObjects(ObjectType: Byte);
3785 var
3786 i: Integer;
3787 IDArray: DWArray;
3788 begin
3789 IDArray := ObjectInRect(rRect.X, rRect.Y,
3790 rRect.Width, rRect.Height,
3791 ObjectType, rSelectRect);
3793 if IDArray <> nil then
3794 for i := 0 to High(IDArray) do
3795 SelectObject(ObjectType, IDArray[i], (ssCtrl in Shift) or rSelectRect);
3796 end;
3797 begin
3798 if Button = mbLeft then
3799 MouseLDown := False;
3800 if Button = mbRight then
3801 MouseRDown := False;
3802 if Button = mbMiddle then
3803 MouseMDown := False;
3805 DrawRect := nil;
3806 ResizeType := RESIZETYPE_NONE;
3807 TextureID := 0;
3809 if Button = mbLeft then // Left Mouse Button
3810 begin
3811 if MouseAction <> MOUSEACTION_NONE then
3812 begin // Было действие мышью
3813 // Мышь сдвинулась во время удержания клавиши,
3814 // либо активирован режим быстрого рисования:
3815 if ((MousePos.X <> MouseLDownPos.X) and
3816 (MousePos.Y <> MouseLDownPos.Y)) or
3817 ((MouseAction in [MOUSEACTION_DRAWPANEL, MOUSEACTION_DRAWTRIGGER]) and
3818 (ssCtrl in Shift)) then
3819 case MouseAction of
3820 // Рисовали панель:
3821 MOUSEACTION_DRAWPANEL:
3822 begin
3823 // Фон или передний план без текстуры - ошибка:
3824 if (lbPanelType.ItemIndex in [1, 2]) and
3825 (lbTextureList.ItemIndex = -1) then
3826 ErrorMessageBox(MsgMsgChooseTexture)
3827 else // Назначаем параметры панели:
3828 begin
3829 case lbPanelType.ItemIndex of
3830 0: Panel.PanelType := PANEL_WALL;
3831 1: Panel.PanelType := PANEL_BACK;
3832 2: Panel.PanelType := PANEL_FORE;
3833 3: Panel.PanelType := PANEL_OPENDOOR;
3834 4: Panel.PanelType := PANEL_CLOSEDOOR;
3835 5: Panel.PanelType := PANEL_STEP;
3836 6: Panel.PanelType := PANEL_WATER;
3837 7: Panel.PanelType := PANEL_ACID1;
3838 8: Panel.PanelType := PANEL_ACID2;
3839 9: Panel.PanelType := PANEL_LIFTUP;
3840 10: Panel.PanelType := PANEL_LIFTDOWN;
3841 11: Panel.PanelType := PANEL_LIFTLEFT;
3842 12: Panel.PanelType := PANEL_LIFTRIGHT;
3843 13: Panel.PanelType := PANEL_BLOCKMON;
3844 end;
3846 Panel.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
3847 Panel.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
3848 if ssCtrl in Shift then
3849 begin
3850 wWidth := DotStep;
3851 wHeight := DotStep;
3852 if (lbTextureList.ItemIndex <> -1) and
3853 (not IsSpecialTextureSel()) then
3854 begin
3855 if not g_GetTexture(SelectedTexture(), TextureID) then
3856 g_GetTexture('NOTEXTURE', TextureID);
3857 g_GetTextureSizeByID(TextureID, wWidth, wHeight);
3858 end;
3859 Panel.Width := wWidth;
3860 Panel.Height := wHeight;
3861 end
3862 else
3863 begin
3864 Panel.Width := Abs(MousePos.X-MouseLDownPos.X);
3865 Panel.Height := Abs(MousePos.Y-MouseLDownPos.Y);
3866 end;
3868 // Лифты, блокМон или отсутствие текстуры - пустая текстура:
3869 if (lbPanelType.ItemIndex in [9, 10, 11, 12, 13]) or
3870 (lbTextureList.ItemIndex = -1) then
3871 begin
3872 Panel.TextureHeight := 1;
3873 Panel.TextureWidth := 1;
3874 Panel.TextureName := '';
3875 Panel.TextureID := TEXTURE_SPECIAL_NONE;
3876 end
3877 else // Есть текстура:
3878 begin
3879 Panel.TextureName := SelectedTexture();
3881 // Обычная текстура:
3882 if not IsSpecialTextureSel() then
3883 begin
3884 g_GetTextureSizeByName(Panel.TextureName,
3885 Panel.TextureWidth, Panel.TextureHeight);
3886 g_GetTexture(Panel.TextureName, Panel.TextureID);
3887 end
3888 else // Спец.текстура:
3889 begin
3890 Panel.TextureHeight := 1;
3891 Panel.TextureWidth := 1;
3892 Panel.TextureID := SpecialTextureID(SelectedTexture());
3893 end;
3894 end;
3896 Panel.Alpha := 0;
3897 Panel.Blending := False;
3899 Undo_Add(OBJECT_PANEL, AddPanel(Panel));
3900 end;
3901 end;
3903 // Рисовали триггер:
3904 MOUSEACTION_DRAWTRIGGER:
3905 begin
3906 trigger.X := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
3907 trigger.Y := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
3908 if ssCtrl in Shift then
3909 begin
3910 wWidth := DotStep;
3911 wHeight := DotStep;
3912 trigger.Width := wWidth;
3913 trigger.Height := wHeight;
3914 end
3915 else
3916 begin
3917 trigger.Width := Abs(MousePos.X-MouseLDownPos.X);
3918 trigger.Height := Abs(MousePos.Y-MouseLDownPos.Y);
3919 end;
3921 trigger.Enabled := True;
3922 trigger.TriggerType := lbTriggersList.ItemIndex+1;
3923 trigger.TexturePanel := -1;
3925 // Типы активации:
3926 trigger.ActivateType := 0;
3928 if clbActivationType.Checked[0] then
3929 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_PLAYERCOLLIDE;
3930 if clbActivationType.Checked[1] then
3931 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_MONSTERCOLLIDE;
3932 if clbActivationType.Checked[2] then
3933 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_PLAYERPRESS;
3934 if clbActivationType.Checked[3] then
3935 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_MONSTERPRESS;
3936 if clbActivationType.Checked[4] then
3937 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_SHOT;
3938 if clbActivationType.Checked[5] then
3939 trigger.ActivateType := Trigger.ActivateType or ACTIVATE_NOMONSTER;
3941 // Необходимые для активации ключи:
3942 trigger.Key := 0;
3944 if clbKeys.Checked[0] then
3945 trigger.Key := Trigger.Key or KEY_RED;
3946 if clbKeys.Checked[1] then
3947 trigger.Key := Trigger.Key or KEY_GREEN;
3948 if clbKeys.Checked[2] then
3949 trigger.Key := Trigger.Key or KEY_BLUE;
3950 if clbKeys.Checked[3] then
3951 trigger.Key := Trigger.Key or KEY_REDTEAM;
3952 if clbKeys.Checked[4] then
3953 trigger.Key := Trigger.Key or KEY_BLUETEAM;
3955 // Параметры триггера:
3956 FillByte(trigger.Data.Default[0], 128, 0);
3958 case trigger.TriggerType of
3959 // Переключаемая панель:
3960 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
3961 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
3962 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
3963 begin
3964 Trigger.Data.PanelID := -1;
3965 end;
3967 // Телепортация:
3968 TRIGGER_TELEPORT:
3969 begin
3970 trigger.Data.TargetPoint.X := trigger.X-64;
3971 trigger.Data.TargetPoint.Y := trigger.Y-64;
3972 trigger.Data.d2d_teleport := True;
3973 trigger.Data.TlpDir := 0;
3974 end;
3976 // Изменение других триггеров:
3977 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF,
3978 TRIGGER_ONOFF:
3979 begin
3980 trigger.Data.Count := 1;
3981 end;
3983 // Звук:
3984 TRIGGER_SOUND:
3985 begin
3986 trigger.Data.Volume := 255;
3987 trigger.Data.Pan := 127;
3988 trigger.Data.PlayCount := 1;
3989 trigger.Data.Local := True;
3990 trigger.Data.SoundSwitch := False;
3991 end;
3993 // Музыка:
3994 TRIGGER_MUSIC:
3995 begin
3996 trigger.Data.MusicAction := 1;
3997 end;
3999 // Создание монстра:
4000 TRIGGER_SPAWNMONSTER:
4001 begin
4002 trigger.Data.MonType := MONSTER_ZOMBY;
4003 trigger.Data.MonPos.X := trigger.X-64;
4004 trigger.Data.MonPos.Y := trigger.Y-64;
4005 trigger.Data.MonHealth := 0;
4006 trigger.Data.MonActive := False;
4007 trigger.Data.MonCount := 1;
4008 end;
4010 // Создание предмета:
4011 TRIGGER_SPAWNITEM:
4012 begin
4013 trigger.Data.ItemType := ITEM_AMMO_BULLETS;
4014 trigger.Data.ItemPos.X := trigger.X-64;
4015 trigger.Data.ItemPos.Y := trigger.Y-64;
4016 trigger.Data.ItemOnlyDM := False;
4017 trigger.Data.ItemFalls := False;
4018 trigger.Data.ItemCount := 1;
4019 trigger.Data.ItemMax := 0;
4020 trigger.Data.ItemDelay := 0;
4021 end;
4023 // Ускорение:
4024 TRIGGER_PUSH:
4025 begin
4026 trigger.Data.PushAngle := 90;
4027 trigger.Data.PushForce := 10;
4028 trigger.Data.ResetVel := True;
4029 end;
4031 TRIGGER_SCORE:
4032 begin
4033 trigger.Data.ScoreCount := 1;
4034 trigger.Data.ScoreCon := True;
4035 trigger.Data.ScoreMsg := True;
4036 end;
4038 TRIGGER_MESSAGE:
4039 begin
4040 trigger.Data.MessageKind := 0;
4041 trigger.Data.MessageSendTo := 0;
4042 trigger.Data.MessageText := '';
4043 trigger.Data.MessageTime := 144;
4044 end;
4046 TRIGGER_DAMAGE:
4047 begin
4048 trigger.Data.DamageValue := 5;
4049 trigger.Data.DamageInterval := 12;
4050 end;
4052 TRIGGER_HEALTH:
4053 begin
4054 trigger.Data.HealValue := 5;
4055 trigger.Data.HealInterval := 36;
4056 end;
4058 TRIGGER_SHOT:
4059 begin
4060 trigger.Data.ShotType := TRIGGER_SHOT_BULLET;
4061 trigger.Data.ShotSound := True;
4062 trigger.Data.ShotPanelID := -1;
4063 trigger.Data.ShotTarget := 0;
4064 trigger.Data.ShotIntSight := 0;
4065 trigger.Data.ShotAim := TRIGGER_SHOT_AIM_DEFAULT;
4066 trigger.Data.ShotPos.X := trigger.X-64;
4067 trigger.Data.ShotPos.Y := trigger.Y-64;
4068 trigger.Data.ShotAngle := 0;
4069 trigger.Data.ShotWait := 18;
4070 trigger.Data.ShotAccuracy := 0;
4071 trigger.Data.ShotAmmo := 0;
4072 trigger.Data.ShotIntReload := 0;
4073 end;
4075 TRIGGER_EFFECT:
4076 begin
4077 trigger.Data.FXCount := 1;
4078 trigger.Data.FXType := TRIGGER_EFFECT_PARTICLE;
4079 trigger.Data.FXSubType := TRIGGER_EFFECT_SLIQUID;
4080 trigger.Data.FXColorR := 0;
4081 trigger.Data.FXColorG := 0;
4082 trigger.Data.FXColorB := 255;
4083 trigger.Data.FXPos := TRIGGER_EFFECT_POS_CENTER;
4084 trigger.Data.FXWait := 1;
4085 trigger.Data.FXVelX := 0;
4086 trigger.Data.FXVelY := -20;
4087 trigger.Data.FXSpreadL := 5;
4088 trigger.Data.FXSpreadR := 5;
4089 trigger.Data.FXSpreadU := 4;
4090 trigger.Data.FXSpreadD := 0;
4091 end;
4092 end;
4094 Undo_Add(OBJECT_TRIGGER, AddTrigger(trigger));
4095 end;
4097 // Рисовали область триггера "Расширитель":
4098 MOUSEACTION_DRAWPRESS:
4099 with gTriggers[SelectedObjects[GetFirstSelected].ID] do
4100 begin
4101 Data.tX := Min(MousePos.X-MapOffset.X, MouseLDownPos.X-MapOffset.X);
4102 Data.tY := Min(MousePos.Y-MapOffset.Y, MouseLDownPos.Y-MapOffset.Y);
4103 Data.tWidth := Abs(MousePos.X-MouseLDownPos.X);
4104 Data.tHeight := Abs(MousePos.Y-MouseLDownPos.Y);
4106 DrawPressRect := False;
4107 end;
4108 end;
4110 MouseAction := MOUSEACTION_NONE;
4111 end;
4112 end // if Button = mbLeft...
4113 else if Button = mbRight then // Right Mouse Button:
4114 begin
4115 if MouseAction = MOUSEACTION_NOACTION then
4116 begin
4117 MouseAction := MOUSEACTION_NONE;
4118 Exit;
4119 end;
4121 // Объект передвинут или изменен в размере:
4122 if MouseAction in [MOUSEACTION_MOVEOBJ, MOUSEACTION_RESIZE] then
4123 begin
4124 RenderPanel.Cursor := crDefault;
4125 MouseAction := MOUSEACTION_NONE;
4126 FillProperty();
4127 Exit;
4128 end;
4130 // Еще не все выбрали:
4131 if SelectFlag <> SELECTFLAG_NONE then
4132 begin
4133 if SelectFlag = SELECTFLAG_SELECTED then
4134 SelectFlag := SELECTFLAG_NONE;
4135 FillProperty();
4136 Exit;
4137 end;
4139 // Мышь сдвинулась во время удержания клавиши:
4140 if (MousePos.X <> MouseRDownPos.X) and
4141 (MousePos.Y <> MouseRDownPos.Y) then
4142 begin
4143 rSelectRect := True;
4145 rRect.X := Min(MousePos.X, MouseRDownPos.X)-MapOffset.X;
4146 rRect.Y := Min(MousePos.Y, MouseRDownPos.Y)-MapOffset.Y;
4147 rRect.Width := Abs(MousePos.X-MouseRDownPos.X);
4148 rRect.Height := Abs(MousePos.Y-MouseRDownPos.Y);
4149 end
4150 else // Мышь не сдвинулась - нет прямоугольника:
4151 begin
4152 rSelectRect := False;
4154 rRect.X := X-MapOffset.X-1;
4155 rRect.Y := Y-MapOffset.Y-1;
4156 rRect.Width := 2;
4157 rRect.Height := 2;
4158 end;
4160 // Если зажат Ctrl - выделять еще, иначе только один выделенный объект:
4161 if not (ssCtrl in Shift) then
4162 RemoveSelectFromObjects();
4164 // Выделяем всё в выбранном прямоугольнике:
4165 if (ssCtrl in Shift) and (ssAlt in Shift) then
4166 begin
4167 SelectObjects(OBJECT_PANEL);
4168 SelectObjects(OBJECT_ITEM);
4169 SelectObjects(OBJECT_MONSTER);
4170 SelectObjects(OBJECT_AREA);
4171 SelectObjects(OBJECT_TRIGGER);
4172 end
4173 else
4174 SelectObjects(pcObjects.ActivePageIndex+1);
4176 FillProperty();
4177 end
4179 else // Middle Mouse Button
4180 begin
4181 RenderPanel.Cursor := crDefault;
4182 ReleaseCapture();
4183 end;
4184 end;
4186 procedure TMainForm.RenderPanelPaint(Sender: TObject);
4187 begin
4188 Draw();
4189 end;
4191 function TMainForm.RenderMousePos(): Types.TPoint;
4192 begin
4193 Result := RenderPanel.ScreenToClient(Mouse.CursorPos);
4194 end;
4196 procedure TMainForm.RecountSelectedObjects();
4197 begin
4198 if SelectedObjectCount() = 0 then
4199 StatusBar.Panels[0].Text := ''
4200 else
4201 StatusBar.Panels[0].Text := Format(MsgCapStatSelected, [SelectedObjectCount()]);
4202 end;
4204 procedure TMainForm.RenderPanelMouseMove(Sender: TObject;
4205 Shift: TShiftState; X, Y: Integer);
4206 var
4207 sX, sY: Integer;
4208 dWidth, dHeight: Integer;
4209 _id: Integer;
4210 TextureID: DWORD;
4211 wWidth, wHeight: Word;
4212 begin
4213 _id := GetFirstSelected();
4214 TextureID := 0;
4216 // Рисуем панель с текстурой, сетка - размеры текстуры:
4217 if (MouseAction = MOUSEACTION_DRAWPANEL) and
4218 (lbPanelType.ItemIndex in [0..8]) and
4219 (lbTextureList.ItemIndex <> -1) and
4220 (not IsSpecialTextureSel()) then
4221 begin
4222 sX := StrToIntDef(lTextureWidth.Caption, DotStep);
4223 sY := StrToIntDef(lTextureHeight.Caption, DotStep);
4224 end
4225 else
4226 // Меняем размер панели с текстурой, сетка - размеры текстуры:
4227 if (MouseAction = MOUSEACTION_RESIZE) and
4228 ( (SelectedObjects[_id].ObjectType = OBJECT_PANEL) and
4229 IsTexturedPanel(gPanels[SelectedObjects[_id].ID].PanelType) and
4230 (gPanels[SelectedObjects[_id].ID].TextureName <> '') and
4231 (not IsSpecialTexture(gPanels[SelectedObjects[_id].ID].TextureName)) ) then
4232 begin
4233 sX := gPanels[SelectedObjects[_id].ID].TextureWidth;
4234 sY := gPanels[SelectedObjects[_id].ID].TextureHeight;
4235 end
4236 else
4237 // Выравнивание по сетке:
4238 if SnapToGrid then
4239 begin
4240 sX := DotStep;
4241 sY := DotStep;
4242 end
4243 else // Нет выравнивания по сетке:
4244 begin
4245 sX := 1;
4246 sY := 1;
4247 end;
4249 // Новая позиция мыши:
4250 if MouseLDown then
4251 begin // Зажата левая кнопка мыши
4252 MousePos.X := (Round((X-MouseLDownPos.X)/sX)*sX)+MouseLDownPos.X;
4253 MousePos.Y := (Round((Y-MouseLDownPos.Y)/sY)*sY)+MouseLDownPos.Y;
4254 end
4255 else
4256 if MouseRDown then
4257 begin // Зажата правая кнопка мыши
4258 MousePos.X := (Round((X-MouseRDownPos.X)/sX)*sX)+MouseRDownPos.X;
4259 MousePos.Y := (Round((Y-MouseRDownPos.Y)/sY)*sY)+MouseRDownPos.Y;
4260 end
4261 else
4262 begin // Кнопки мыши не зажаты
4263 MousePos.X := Round((-MapOffset.X + X) / sX) * sX + MapOffset.X;
4264 MousePos.Y := Round((-MapOffset.Y + Y) / sY) * sY + MapOffset.Y;
4265 end;
4267 // Зажата только правая кнопка мыши:
4268 if (not MouseLDown) and (MouseRDown) and (not MouseMDown) then
4269 begin
4270 // Рисуем прямоугольник выделения:
4271 if MouseAction = MOUSEACTION_NONE then
4272 begin
4273 if DrawRect = nil then
4274 New(DrawRect);
4275 DrawRect.Top := MouseRDownPos.y;
4276 DrawRect.Left := MouseRDownPos.x;
4277 DrawRect.Bottom := MousePos.y;
4278 DrawRect.Right := MousePos.x;
4279 end
4280 else
4281 // Двигаем выделенные объекты:
4282 if MouseAction = MOUSEACTION_MOVEOBJ then
4283 begin
4284 MoveSelectedObjects(ssShift in Shift, ssCtrl in Shift,
4285 MousePos.X-LastMovePoint.X,
4286 MousePos.Y-LastMovePoint.Y);
4287 end
4288 else
4289 // Меняем размер выделенного объекта:
4290 if MouseAction = MOUSEACTION_RESIZE then
4291 begin
4292 if (SelectedObjectCount = 1) and
4293 (SelectedObjects[GetFirstSelected].Live) then
4294 begin
4295 dWidth := MousePos.X-LastMovePoint.X;
4296 dHeight := MousePos.Y-LastMovePoint.Y;
4298 case ResizeType of
4299 RESIZETYPE_VERTICAL: dWidth := 0;
4300 RESIZETYPE_HORIZONTAL: dHeight := 0;
4301 end;
4303 case ResizeDirection of
4304 RESIZEDIR_UP: dHeight := -dHeight;
4305 RESIZEDIR_LEFT: dWidth := -dWidth;
4306 end;
4308 if ResizeObject(SelectedObjects[GetFirstSelected].ObjectType,
4309 SelectedObjects[GetFirstSelected].ID,
4310 dWidth, dHeight, ResizeDirection) then
4311 LastMovePoint := MousePos;
4312 end;
4313 end;
4314 end;
4316 // Зажата только левая кнопка мыши:
4317 if (not MouseRDown) and (MouseLDown) and (not MouseMDown) then
4318 begin
4319 // Рисуем прямоугольник планирования панели:
4320 if MouseAction in [MOUSEACTION_DRAWPANEL,
4321 MOUSEACTION_DRAWTRIGGER,
4322 MOUSEACTION_DRAWPRESS] then
4323 begin
4324 if DrawRect = nil then
4325 New(DrawRect);
4326 if ssCtrl in Shift then
4327 begin
4328 wWidth := DotStep;
4329 wHeight := DotStep;
4330 if (lbTextureList.ItemIndex <> -1) and (not IsSpecialTextureSel()) and
4331 (MouseAction = MOUSEACTION_DRAWPANEL) then
4332 begin
4333 if not g_GetTexture(SelectedTexture(), TextureID) then
4334 g_GetTexture('NOTEXTURE', TextureID);
4335 g_GetTextureSizeByID(TextureID, wWidth, wHeight);
4336 end;
4337 DrawRect.Top := MouseLDownPos.y;
4338 DrawRect.Left := MouseLDownPos.x;
4339 DrawRect.Bottom := DrawRect.Top + wHeight;
4340 DrawRect.Right := DrawRect.Left + wWidth;
4341 end
4342 else
4343 begin
4344 DrawRect.Top := MouseLDownPos.y;
4345 DrawRect.Left := MouseLDownPos.x;
4346 DrawRect.Bottom := MousePos.y;
4347 DrawRect.Right := MousePos.x;
4348 end;
4349 end
4350 else // Двигаем карту:
4351 if MouseAction = MOUSEACTION_MOVEMAP then
4352 begin
4353 MoveMap(X, Y);
4354 end;
4355 end;
4357 // Only Middle Mouse Button is pressed
4358 if (not MouseLDown) and (not MouseRDown) and (MouseMDown) then
4359 begin
4360 MapOffset.X := -EnsureRange(-MapOffset.X + MouseMDownPos.X - Mouse.CursorPos.X,
4361 sbHorizontal.Min, sbHorizontal.Max);
4362 sbHorizontal.Position := -MapOffset.X;
4363 MapOffset.Y := -EnsureRange(-MapOffset.Y + MouseMDownPos.Y - Mouse.CursorPos.Y,
4364 sbVertical.Min, sbVertical.Max);
4365 sbVertical.Position := -MapOffset.Y;
4366 MouseMDownPos := Mouse.CursorPos;
4367 end;
4369 // Клавиши мыши не зажаты:
4370 if (not MouseRDown) and (not MouseLDown) then
4371 DrawRect := nil;
4373 // Строка состояния - координаты мыши:
4374 StatusBar.Panels[1].Text := Format('(%d:%d)',
4375 [MousePos.X-MapOffset.X, MousePos.Y-MapOffset.Y]);
4377 RenderPanel.Invalidate;
4378 end;
4380 procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
4381 begin
4382 CanClose := Application.MessageBox(PChar(MsgMsgExitPromt),
4383 PChar(MsgMsgExit),
4384 MB_ICONQUESTION or MB_YESNO or
4385 MB_DEFBUTTON1) = idYes;
4386 end;
4388 procedure TMainForm.aExitExecute(Sender: TObject);
4389 begin
4390 Close();
4391 end;
4393 procedure TMainForm.FormDestroy(Sender: TObject);
4394 var
4395 config: TConfig;
4396 s: AnsiString;
4397 i: Integer;
4398 begin
4399 config := TConfig.CreateFile(CfgFileName);
4401 if WindowState <> wsMaximized then
4402 begin
4403 config.WriteInt('Editor', 'XPos', Left);
4404 config.WriteInt('Editor', 'YPos', Top);
4405 config.WriteInt('Editor', 'Width', Width);
4406 config.WriteInt('Editor', 'Height', Height);
4407 end
4408 else
4409 begin
4410 config.WriteInt('Editor', 'XPos', RestoredLeft);
4411 config.WriteInt('Editor', 'YPos', RestoredTop);
4412 config.WriteInt('Editor', 'Width', RestoredWidth);
4413 config.WriteInt('Editor', 'Height', RestoredHeight);
4414 end;
4415 config.WriteBool('Editor', 'Maximize', WindowState = wsMaximized);
4416 config.WriteBool('Editor', 'Minimap', ShowMap);
4417 config.WriteInt('Editor', 'PanelProps', PanelProps.ClientWidth);
4418 config.WriteInt('Editor', 'PanelObjs', PanelObjs.ClientHeight);
4419 config.WriteBool('Editor', 'DotEnable', DotEnable);
4420 config.WriteInt('Editor', 'DotStep', DotStep);
4421 config.WriteStr('Editor', 'LastOpenDir', OpenDialog.InitialDir);
4422 config.WriteStr('Editor', 'LastSaveDir', SaveDialog.InitialDir);
4423 config.WriteStr('Editor', 'Language', gLanguage);
4424 config.WriteBool('Editor', 'EdgeShow', drEdge[3] < 255);
4425 config.WriteInt('Editor', 'EdgeColor', gColorEdge);
4426 config.WriteInt('Editor', 'EdgeAlpha', gAlphaEdge);
4427 config.WriteInt('Editor', 'LineAlpha', gAlphaTriggerLine);
4428 config.WriteInt('Editor', 'TriggerAlpha', gAlphaTriggerArea);
4429 config.WriteInt('Editor', 'MonsterRectAlpha', gAlphaMonsterRect);
4430 config.WriteInt('Editor', 'AreaRectAlpha', gAlphaAreaRect);
4432 for i := 0 to RecentCount - 1 do
4433 begin
4434 if i < RecentFiles.Count then s := RecentFiles[i] else s := '';
4435 {$IFDEF WINDOWS}
4436 config.WriteStr('RecentFilesWin', IntToStr(i), s);
4437 {$ELSE}
4438 config.WriteStr('RecentFilesUnix', IntToStr(i), s);
4439 {$ENDIF}
4440 end;
4441 RecentFiles.Free();
4443 config.SaveFile(CfgFileName);
4444 config.Free();
4446 slInvalidTextures.Free;
4447 end;
4449 procedure TMainForm.FormDropFiles(Sender: TObject;
4450 const FileNames: array of String);
4451 begin
4452 if Length(FileNames) <> 1 then
4453 Exit;
4455 OpenMapFile(FileNames[0]);
4456 end;
4458 procedure TMainForm.RenderPanelResize(Sender: TObject);
4459 begin
4460 if MainForm.Visible then
4461 MainForm.Resize();
4462 end;
4464 procedure TMainForm.Splitter1Moved(Sender: TObject);
4465 begin
4466 FormResize(Sender);
4467 end;
4469 procedure TMainForm.MapTestCheck(Sender: TObject);
4470 begin
4471 if MapTestProcess <> nil then
4472 begin
4473 if MapTestProcess.Running = false then
4474 begin
4475 if MapTestProcess.ExitCode <> 0 then
4476 Application.MessageBox(PChar(MsgMsgExecError), 'FIXME', MB_OK or MB_ICONERROR);
4477 SysUtils.DeleteFile(MapTestFile);
4478 MapTestFile := '';
4479 FreeAndNil(MapTestProcess);
4480 tbTestMap.Enabled := True;
4481 end;
4482 end;
4483 end;
4485 procedure TMainForm.aMapOptionsExecute(Sender: TObject);
4486 var
4487 ResName: String;
4488 begin
4489 MapOptionsForm.ShowModal();
4491 ResName := OpenedMap;
4492 while (Pos(':\', ResName) > 0) do
4493 Delete(ResName, 1, Pos(':\', ResName) + 1);
4495 UpdateCaption(gMapInfo.Name, ExtractFileName(OpenedWAD), ResName);
4496 end;
4498 procedure TMainForm.aAboutExecute(Sender: TObject);
4499 begin
4500 AboutForm.ShowModal();
4501 end;
4503 procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
4504 var
4505 dx, dy, i: Integer;
4506 FileName: String;
4507 ok: Boolean;
4508 begin
4509 if (not EditingProperties) then
4510 begin
4511 if ssCtrl in Shift then
4512 begin
4513 case Chr(Key) of
4514 '1': ContourEnabled[LAYER_BACK] := not ContourEnabled[LAYER_BACK];
4515 '2': ContourEnabled[LAYER_WALLS] := not ContourEnabled[LAYER_WALLS];
4516 '3': ContourEnabled[LAYER_FOREGROUND] := not ContourEnabled[LAYER_FOREGROUND];
4517 '4': ContourEnabled[LAYER_STEPS] := not ContourEnabled[LAYER_STEPS];
4518 '5': ContourEnabled[LAYER_WATER] := not ContourEnabled[LAYER_WATER];
4519 '6': ContourEnabled[LAYER_ITEMS] := not ContourEnabled[LAYER_ITEMS];
4520 '7': ContourEnabled[LAYER_MONSTERS] := not ContourEnabled[LAYER_MONSTERS];
4521 '8': ContourEnabled[LAYER_AREAS] := not ContourEnabled[LAYER_AREAS];
4522 '9': ContourEnabled[LAYER_TRIGGERS] := not ContourEnabled[LAYER_TRIGGERS];
4523 '0':
4524 begin
4525 ok := False;
4526 for i := Low(ContourEnabled) to High(ContourEnabled) do
4527 if ContourEnabled[i] then
4528 ok := True;
4529 for i := Low(ContourEnabled) to High(ContourEnabled) do
4530 ContourEnabled[i] := not ok
4531 end
4532 end
4533 end
4534 else
4535 begin
4536 case Chr(key) of
4537 '1': SwitchLayer(LAYER_BACK);
4538 '2': SwitchLayer(LAYER_WALLS);
4539 '3': SwitchLayer(LAYER_FOREGROUND);
4540 '4': SwitchLayer(LAYER_STEPS);
4541 '5': SwitchLayer(LAYER_WATER);
4542 '6': SwitchLayer(LAYER_ITEMS);
4543 '7': SwitchLayer(LAYER_MONSTERS);
4544 '8': SwitchLayer(LAYER_AREAS);
4545 '9': SwitchLayer(LAYER_TRIGGERS);
4546 '0': tbShowClick(tbShow);
4547 end
4548 end;
4550 if Key = Ord('V') then
4551 begin // Поворот монстров и областей:
4552 if (SelectedObjects <> nil) then
4553 begin
4554 for i := 0 to High(SelectedObjects) do
4555 if (SelectedObjects[i].Live) then
4556 begin
4557 if (SelectedObjects[i].ObjectType = OBJECT_MONSTER) then
4558 begin
4559 g_ChangeDir(gMonsters[SelectedObjects[i].ID].Direction);
4560 end
4561 else
4562 if (SelectedObjects[i].ObjectType = OBJECT_AREA) then
4563 begin
4564 g_ChangeDir(gAreas[SelectedObjects[i].ID].Direction);
4565 end;
4566 end;
4567 end
4568 else
4569 begin
4570 if pcObjects.ActivePage = tsMonsters then
4571 begin
4572 if rbMonsterLeft.Checked then
4573 rbMonsterRight.Checked := True
4574 else
4575 rbMonsterLeft.Checked := True;
4576 end;
4577 if pcObjects.ActivePage = tsAreas then
4578 begin
4579 if rbAreaLeft.Checked then
4580 rbAreaRight.Checked := True
4581 else
4582 rbAreaLeft.Checked := True;
4583 end;
4584 end;
4585 end;
4587 if not (ssCtrl in Shift) then
4588 begin
4589 // Быстрое превью карты:
4590 if Key = Ord('E') then
4591 begin
4592 if PreviewMode = 0 then
4593 PreviewMode := 2;
4594 end;
4596 // Вертикальный скролл карты:
4597 with sbVertical do
4598 begin
4599 if Key = Ord('W') then
4600 begin
4601 dy := Position;
4602 if ssShift in Shift then Position := EnsureRange(Position - DotStep * 4, Min, Max)
4603 else Position := EnsureRange(Position - DotStep, Min, Max);
4604 MapOffset.Y := -Position;
4605 dy -= Position;
4607 if (MouseLDown or MouseRDown) then
4608 begin
4609 if DrawRect <> nil then
4610 begin
4611 Inc(MouseLDownPos.y, dy);
4612 Inc(MouseRDownPos.y, dy);
4613 end;
4614 Inc(LastMovePoint.Y, dy);
4615 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4616 end;
4617 end;
4619 if Key = Ord('S') then
4620 begin
4621 dy := Position;
4622 if ssShift in Shift then Position := EnsureRange(Position + DotStep * 4, Min, Max)
4623 else Position := EnsureRange(Position + DotStep, Min, Max);
4624 MapOffset.Y := -Position;
4625 dy -= Position;
4627 if (MouseLDown or MouseRDown) then
4628 begin
4629 if DrawRect <> nil then
4630 begin
4631 Inc(MouseLDownPos.y, dy);
4632 Inc(MouseRDownPos.y, dy);
4633 end;
4634 Inc(LastMovePoint.Y, dy);
4635 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4636 end;
4637 end;
4638 end;
4640 // Горизонтальный скролл карты:
4641 with sbHorizontal do
4642 begin
4643 if Key = Ord('A') then
4644 begin
4645 dx := Position;
4646 if ssShift in Shift then Position := EnsureRange(Position - DotStep * 4, Min, Max)
4647 else Position := EnsureRange(Position - DotStep, Min, Max);
4648 MapOffset.X := -Position;
4649 dx -= Position;
4651 if (MouseLDown or MouseRDown) then
4652 begin
4653 if DrawRect <> nil then
4654 begin
4655 Inc(MouseLDownPos.x, dx);
4656 Inc(MouseRDownPos.x, dx);
4657 end;
4658 Inc(LastMovePoint.X, dx);
4659 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4660 end;
4661 end;
4663 if Key = Ord('D') then
4664 begin
4665 dx := Position;
4666 if ssShift in Shift then Position := EnsureRange(Position + DotStep * 4, Min, Max)
4667 else Position := EnsureRange(Position + DotStep, Min, Max);
4668 MapOffset.X := -Position;
4669 dx -= Position;
4671 if (MouseLDown or MouseRDown) then
4672 begin
4673 if DrawRect <> nil then
4674 begin
4675 Inc(MouseLDownPos.x, dx);
4676 Inc(MouseRDownPos.x, dx);
4677 end;
4678 Inc(LastMovePoint.X, dx);
4679 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4680 end;
4681 end;
4682 end;
4683 end
4684 else // ssCtrl in Shift
4685 begin
4686 if ssShift in Shift then
4687 begin
4688 // Вставка по абсолютному смещению:
4689 if Key = Ord('V') then
4690 aPasteObjectExecute(Sender);
4691 end;
4692 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
4693 end;
4694 end;
4696 // Удалить выделенные объекты:
4697 if (Key = VK_DELETE) and (SelectedObjects <> nil) and
4698 RenderPanel.Focused() then
4699 DeleteSelectedObjects();
4701 // Снять выделение:
4702 if (Key = VK_ESCAPE) and (SelectedObjects <> nil) then
4703 RemoveSelectFromObjects();
4705 // Передвинуть объекты:
4706 if MainForm.ActiveControl = RenderPanel then
4707 begin
4708 dx := 0;
4709 dy := 0;
4711 if Key = VK_NUMPAD4 then
4712 dx := IfThen(ssAlt in Shift, -1, -DotStep);
4713 if Key = VK_NUMPAD6 then
4714 dx := IfThen(ssAlt in Shift, 1, DotStep);
4715 if Key = VK_NUMPAD8 then
4716 dy := IfThen(ssAlt in Shift, -1, -DotStep);
4717 if Key = VK_NUMPAD5 then
4718 dy := IfThen(ssAlt in Shift, 1, DotStep);
4720 if (dx <> 0) or (dy <> 0) then
4721 begin
4722 MoveSelectedObjects(ssShift in Shift, ssCtrl in Shift, dx, dy);
4723 Key := 0;
4724 end;
4725 end;
4727 if ssCtrl in Shift then
4728 begin
4729 // Выбор панели с текстурой для триггера
4730 if Key = Ord('T') then
4731 begin
4732 DrawPressRect := False;
4733 if SelectFlag = SELECTFLAG_TEXTURE then
4734 begin
4735 SelectFlag := SELECTFLAG_NONE;
4736 Exit;
4737 end;
4738 vleObjectProperty.FindRow(MsgPropTrTexturePanel, i);
4739 if i > 0 then
4740 SelectFlag := SELECTFLAG_TEXTURE;
4741 end;
4743 if Key = Ord('D') then
4744 begin
4745 SelectFlag := SELECTFLAG_NONE;
4746 if DrawPressRect then
4747 begin
4748 DrawPressRect := False;
4749 Exit;
4750 end;
4751 i := -1;
4753 // Выбор области воздействия, в зависимости от типа триггера
4754 vleObjectProperty.FindRow(MsgPropTrExArea, i);
4755 if i > 0 then
4756 begin
4757 DrawPressRect := True;
4758 Exit;
4759 end;
4760 vleObjectProperty.FindRow(MsgPropTrDoorPanel, i);
4761 if i <= 0 then
4762 vleObjectProperty.FindRow(MsgPropTrTrapPanel, i);
4763 if i > 0 then
4764 begin
4765 SelectFlag := SELECTFLAG_DOOR;
4766 Exit;
4767 end;
4768 vleObjectProperty.FindRow(MsgPropTrLiftPanel, i);
4769 if i > 0 then
4770 begin
4771 SelectFlag := SELECTFLAG_LIFT;
4772 Exit;
4773 end;
4774 vleObjectProperty.FindRow(MsgPropTrTeleportTo, i);
4775 if i > 0 then
4776 begin
4777 SelectFlag := SELECTFLAG_TELEPORT;
4778 Exit;
4779 end;
4780 vleObjectProperty.FindRow(MsgPropTrSpawnTo, i);
4781 if i > 0 then
4782 begin
4783 SelectFlag := SELECTFLAG_SPAWNPOINT;
4784 Exit;
4785 end;
4787 // Выбор основного параметра, в зависимости от типа триггера
4788 vleObjectProperty.FindRow(MsgPropTrNextMap, i);
4789 if i > 0 then
4790 begin
4791 g_ProcessResourceStr(OpenedMap, @FileName, nil, nil);
4792 SelectMapForm.Caption := MsgCapSelect;
4793 SelectMapForm.GetMaps(FileName);
4795 if SelectMapForm.ShowModal() = mrOK then
4796 begin
4797 vleObjectProperty.Cells[1, i] := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
4798 bApplyProperty.Click();
4799 end;
4800 Exit;
4801 end;
4802 vleObjectProperty.FindRow(MsgPropTrSoundName, i);
4803 if i <= 0 then
4804 vleObjectProperty.FindRow(MsgPropTrMusicName, i);
4805 if i > 0 then
4806 begin
4807 AddSoundForm.OKFunction := nil;
4808 AddSoundForm.lbResourcesList.MultiSelect := False;
4809 AddSoundForm.SetResource := vleObjectProperty.Cells[1, i];
4811 if (AddSoundForm.ShowModal() = mrOk) then
4812 begin
4813 vleObjectProperty.Cells[1, i] := AddSoundForm.ResourceName;
4814 bApplyProperty.Click();
4815 end;
4816 Exit;
4817 end;
4818 vleObjectProperty.FindRow(MsgPropTrPushAngle, i);
4819 if i <= 0 then
4820 vleObjectProperty.FindRow(MsgPropTrMessageText, i);
4821 if i > 0 then
4822 begin
4823 vleObjectProperty.Row := i;
4824 vleObjectProperty.SetFocus();
4825 Exit;
4826 end;
4827 end;
4828 end;
4829 end;
4831 procedure TMainForm.aOptimizeExecute(Sender: TObject);
4832 begin
4833 RemoveSelectFromObjects();
4834 MapOptimizationForm.ShowModal();
4835 end;
4837 procedure TMainForm.aCheckMapExecute(Sender: TObject);
4838 begin
4839 MapCheckForm.ShowModal();
4840 end;
4842 procedure TMainForm.bbAddTextureClick(Sender: TObject);
4843 begin
4844 AddTextureForm.lbResourcesList.MultiSelect := True;
4845 AddTextureForm.ShowModal();
4846 end;
4848 procedure TMainForm.lbTextureListClick(Sender: TObject);
4849 var
4850 TextureID: DWORD;
4851 TextureWidth, TextureHeight: Word;
4852 begin
4853 TextureID := 0;
4854 TextureWidth := 0;
4855 TextureHeight := 0;
4856 if (lbTextureList.ItemIndex <> -1) and
4857 (not IsSpecialTextureSel()) then
4858 begin
4859 if g_GetTexture(SelectedTexture(), TextureID) then
4860 begin
4861 g_GetTextureSizeByID(TextureID, TextureWidth, TextureHeight);
4863 lTextureWidth.Caption := IntToStr(TextureWidth);
4864 lTextureHeight.Caption := IntToStr(TextureHeight);
4865 end else
4866 begin
4867 lTextureWidth.Caption := MsgNotAccessible;
4868 lTextureHeight.Caption := MsgNotAccessible;
4869 end;
4870 end
4871 else
4872 begin
4873 lTextureWidth.Caption := '';
4874 lTextureHeight.Caption := '';
4875 end;
4876 end;
4878 procedure TMainForm.lbTextureListDrawItem(Control: TWinControl; Index: Integer;
4879 ARect: TRect; State: TOwnerDrawState);
4880 begin
4881 with Control as TListBox do
4882 begin
4883 if LCLType.odSelected in State then
4884 begin
4885 Canvas.Brush.Color := clHighlight;
4886 Canvas.Font.Color := clHighlightText;
4887 end else
4888 if (Items <> nil) and (Index >= 0) then
4889 if slInvalidTextures.IndexOf(Items[Index]) > -1 then
4890 begin
4891 Canvas.Brush.Color := clRed;
4892 Canvas.Font.Color := clWhite;
4893 end;
4894 Canvas.FillRect(ARect);
4895 Canvas.TextRect(ARect, ARect.Left, ARect.Top, Items[Index]);
4896 end;
4897 end;
4899 procedure TMainForm.miMacMinimizeClick(Sender: TObject);
4900 begin
4901 self.WindowState := wsMinimized;
4902 self.FormWindowStateChange(Sender);
4903 end;
4905 procedure TMainForm.miMacZoomClick(Sender: TObject);
4906 begin
4907 if self.WindowState = wsMaximized then
4908 self.WindowState := wsNormal
4909 else
4910 self.WindowState := wsMaximized;
4911 self.FormWindowStateChange(Sender);
4912 end;
4914 procedure TMainForm.miReopenMapClick(Sender: TObject);
4915 var
4916 FileName, Resource: String;
4917 begin
4918 if OpenedMap = '' then
4919 Exit;
4921 if Application.MessageBox(PChar(MsgMsgReopenMapPromt),
4922 PChar(MsgMenuFileReopen), MB_ICONQUESTION or MB_YESNO) <> idYes then
4923 Exit;
4925 g_ProcessResourceStr(OpenedMap, @FileName, nil, @Resource);
4926 OpenMap(FileName, Resource);
4927 end;
4929 procedure TMainForm.vleObjectPropertyGetPickList(Sender: TObject;
4930 const KeyName: String; Values: TStrings);
4931 begin
4932 if vleObjectProperty.ItemProps[KeyName].EditStyle = esPickList then
4933 begin
4934 if KeyName = MsgPropDirection then
4935 begin
4936 Values.Add(DirNames[D_LEFT]);
4937 Values.Add(DirNames[D_RIGHT]);
4938 end
4939 else if KeyName = MsgPropTrTeleportDir then
4940 begin
4941 Values.Add(DirNamesAdv[0]);
4942 Values.Add(DirNamesAdv[1]);
4943 Values.Add(DirNamesAdv[2]);
4944 Values.Add(DirNamesAdv[3]);
4945 end
4946 else if KeyName = MsgPropTrMusicAct then
4947 begin
4948 Values.Add(MsgPropTrMusicOn);
4949 Values.Add(MsgPropTrMusicOff);
4950 end
4951 else if KeyName = MsgPropTrMonsterBehaviour then
4952 begin
4953 Values.Add(MsgPropTrMonsterBehaviour0);
4954 Values.Add(MsgPropTrMonsterBehaviour1);
4955 Values.Add(MsgPropTrMonsterBehaviour2);
4956 Values.Add(MsgPropTrMonsterBehaviour3);
4957 Values.Add(MsgPropTrMonsterBehaviour4);
4958 Values.Add(MsgPropTrMonsterBehaviour5);
4959 end
4960 else if KeyName = MsgPropTrScoreAct then
4961 begin
4962 Values.Add(MsgPropTrScoreAct0);
4963 Values.Add(MsgPropTrScoreAct1);
4964 Values.Add(MsgPropTrScoreAct2);
4965 Values.Add(MsgPropTrScoreAct3);
4966 end
4967 else if KeyName = MsgPropTrScoreTeam then
4968 begin
4969 Values.Add(MsgPropTrScoreTeam0);
4970 Values.Add(MsgPropTrScoreTeam1);
4971 Values.Add(MsgPropTrScoreTeam2);
4972 Values.Add(MsgPropTrScoreTeam3);
4973 end
4974 else if KeyName = MsgPropTrMessageKind then
4975 begin
4976 Values.Add(MsgPropTrMessageKind0);
4977 Values.Add(MsgPropTrMessageKind1);
4978 end
4979 else if KeyName = MsgPropTrMessageTo then
4980 begin
4981 Values.Add(MsgPropTrMessageTo0);
4982 Values.Add(MsgPropTrMessageTo1);
4983 Values.Add(MsgPropTrMessageTo2);
4984 Values.Add(MsgPropTrMessageTo3);
4985 Values.Add(MsgPropTrMessageTo4);
4986 Values.Add(MsgPropTrMessageTo5);
4987 end
4988 else if KeyName = MsgPropTrShotTo then
4989 begin
4990 Values.Add(MsgPropTrShotTo0);
4991 Values.Add(MsgPropTrShotTo1);
4992 Values.Add(MsgPropTrShotTo2);
4993 Values.Add(MsgPropTrShotTo3);
4994 Values.Add(MsgPropTrShotTo4);
4995 Values.Add(MsgPropTrShotTo5);
4996 Values.Add(MsgPropTrShotTo6);
4997 end
4998 else if KeyName = MsgPropTrShotAim then
4999 begin
5000 Values.Add(MsgPropTrShotAim0);
5001 Values.Add(MsgPropTrShotAim1);
5002 Values.Add(MsgPropTrShotAim2);
5003 Values.Add(MsgPropTrShotAim3);
5004 end
5005 else if KeyName = MsgPropTrDamageKind then
5006 begin
5007 Values.Add(MsgPropTrDamageKind0);
5008 Values.Add(MsgPropTrDamageKind3);
5009 Values.Add(MsgPropTrDamageKind4);
5010 Values.Add(MsgPropTrDamageKind5);
5011 Values.Add(MsgPropTrDamageKind6);
5012 Values.Add(MsgPropTrDamageKind7);
5013 Values.Add(MsgPropTrDamageKind8);
5014 end
5015 else if (KeyName = MsgPropPanelBlend) or
5016 (KeyName = MsgPropDmOnly) or
5017 (KeyName = MsgPropItemFalls) or
5018 (KeyName = MsgPropTrEnabled) or
5019 (KeyName = MsgPropTrD2d) or
5020 (KeyName = MsgPropTrSilent) or
5021 (KeyName = MsgPropTrTeleportSilent) or
5022 (KeyName = MsgPropTrExRandom) or
5023 (KeyName = MsgPropTrTextureOnce) or
5024 (KeyName = MsgPropTrTextureAnimOnce) or
5025 (KeyName = MsgPropTrSoundLocal) or
5026 (KeyName = MsgPropTrSoundSwitch) or
5027 (KeyName = MsgPropTrMonsterActive) or
5028 (KeyName = MsgPropTrPushReset) or
5029 (KeyName = MsgPropTrScoreCon) or
5030 (KeyName = MsgPropTrScoreMsg) or
5031 (KeyName = MsgPropTrHealthMax) or
5032 (KeyName = MsgPropTrShotSound) or
5033 (KeyName = MsgPropTrEffectCenter) then
5034 begin
5035 Values.Add(BoolNames[True]);
5036 Values.Add(BoolNames[False]);
5037 end;
5038 end;
5039 end;
5041 procedure TMainForm.bApplyPropertyClick(Sender: TObject);
5042 var
5043 _id, a, r, c: Integer;
5044 s: String;
5045 res: Boolean;
5046 NoTextureID: DWORD;
5047 NW, NH: Word;
5048 begin
5049 NoTextureID := 0;
5050 NW := 0;
5051 NH := 0;
5053 if SelectedObjectCount() <> 1 then
5054 Exit;
5055 if not SelectedObjects[GetFirstSelected()].Live then
5056 Exit;
5058 try
5059 if not CheckProperty() then
5060 Exit;
5061 except
5062 Exit;
5063 end;
5065 _id := GetFirstSelected();
5067 r := vleObjectProperty.Row;
5068 c := vleObjectProperty.Col;
5070 case SelectedObjects[_id].ObjectType of
5071 OBJECT_PANEL:
5072 begin
5073 with gPanels[SelectedObjects[_id].ID] do
5074 begin
5075 X := StrToInt(Trim(vleObjectProperty.Values[MsgPropX]));
5076 Y := StrToInt(Trim(vleObjectProperty.Values[MsgPropY]));
5077 Width := StrToInt(Trim(vleObjectProperty.Values[MsgPropWidth]));
5078 Height := StrToInt(Trim(vleObjectProperty.Values[MsgPropHeight]));
5080 PanelType := GetPanelType(vleObjectProperty.Values[MsgPropPanelType]);
5082 // Сброс ссылки на триггеры смены текстуры:
5083 if not WordBool(PanelType and (PANEL_WALL or PANEL_FORE or PANEL_BACK)) then
5084 if gTriggers <> nil then
5085 for a := 0 to High(gTriggers) do
5086 begin
5087 if (gTriggers[a].TriggerType <> 0) and
5088 (gTriggers[a].TexturePanel = Integer(SelectedObjects[_id].ID)) then
5089 gTriggers[a].TexturePanel := -1;
5090 if (gTriggers[a].TriggerType = TRIGGER_SHOT) and
5091 (gTriggers[a].Data.ShotPanelID = Integer(SelectedObjects[_id].ID)) then
5092 gTriggers[a].Data.ShotPanelID := -1;
5093 end;
5095 // Сброс ссылки на триггеры лифта:
5096 if not WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
5097 if gTriggers <> nil then
5098 for a := 0 to High(gTriggers) do
5099 if (gTriggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT]) and
5100 (gTriggers[a].Data.PanelID = Integer(SelectedObjects[_id].ID)) then
5101 gTriggers[a].Data.PanelID := -1;
5103 // Сброс ссылки на триггеры двери:
5104 if not WordBool(PanelType and (PANEL_OPENDOOR or PANEL_CLOSEDOOR)) then
5105 if gTriggers <> nil then
5106 for a := 0 to High(gTriggers) do
5107 if (gTriggers[a].TriggerType in [TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
5108 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP]) and
5109 (gTriggers[a].Data.PanelID = Integer(SelectedObjects[_id].ID)) then
5110 gTriggers[a].Data.PanelID := -1;
5112 if IsTexturedPanel(PanelType) then
5113 begin // Может быть текстура
5114 if TextureName <> '' then
5115 begin // Была текстура
5116 Alpha := StrToInt(Trim(vleObjectProperty.Values[MsgPropPanelAlpha]));
5117 Blending := NameToBool(vleObjectProperty.Values[MsgPropPanelBlend]);
5118 end
5119 else // Не было
5120 begin
5121 Alpha := 0;
5122 Blending := False;
5123 end;
5125 // Новая текстура:
5126 TextureName := vleObjectProperty.Values[MsgPropPanelTex];
5128 if TextureName <> '' then
5129 begin // Есть текстура
5130 // Обычная текстура:
5131 if not IsSpecialTexture(TextureName) then
5132 begin
5133 g_GetTextureSizeByName(TextureName,
5134 TextureWidth, TextureHeight);
5136 // Проверка кратности размеров панели:
5137 res := True;
5138 if TextureWidth <> 0 then
5139 if gPanels[SelectedObjects[_id].ID].Width mod TextureWidth <> 0 then
5140 begin
5141 ErrorMessageBox(Format(MsgMsgWrongTexwidth,
5142 [TextureWidth]));
5143 Res := False;
5144 end;
5145 if Res and (TextureHeight <> 0) then
5146 if gPanels[SelectedObjects[_id].ID].Height mod TextureHeight <> 0 then
5147 begin
5148 ErrorMessageBox(Format(MsgMsgWrongTexheight,
5149 [TextureHeight]));
5150 Res := False;
5151 end;
5153 if Res then
5154 begin
5155 if not g_GetTexture(TextureName, TextureID) then
5156 // Не удалось загрузить текстуру, рисуем NOTEXTURE
5157 if g_GetTexture('NOTEXTURE', NoTextureID) then
5158 begin
5159 TextureID := TEXTURE_SPECIAL_NOTEXTURE;
5160 g_GetTextureSizeByID(NoTextureID, NW, NH);
5161 TextureWidth := NW;
5162 TextureHeight := NH;
5163 end else
5164 begin
5165 TextureID := TEXTURE_SPECIAL_NONE;
5166 TextureWidth := 1;
5167 TextureHeight := 1;
5168 end;
5169 end
5170 else
5171 begin
5172 TextureName := '';
5173 TextureWidth := 1;
5174 TextureHeight := 1;
5175 TextureID := TEXTURE_SPECIAL_NONE;
5176 end;
5177 end
5178 else // Спец.текстура
5179 begin
5180 TextureHeight := 1;
5181 TextureWidth := 1;
5182 TextureID := SpecialTextureID(TextureName);
5183 end;
5184 end
5185 else // Нет текстуры
5186 begin
5187 TextureWidth := 1;
5188 TextureHeight := 1;
5189 TextureID := TEXTURE_SPECIAL_NONE;
5190 end;
5191 end
5192 else // Не может быть текстуры
5193 begin
5194 Alpha := 0;
5195 Blending := False;
5196 TextureName := '';
5197 TextureWidth := 1;
5198 TextureHeight := 1;
5199 TextureID := TEXTURE_SPECIAL_NONE;
5200 end;
5201 end;
5202 end;
5204 OBJECT_ITEM:
5205 begin
5206 with gItems[SelectedObjects[_id].ID] do
5207 begin
5208 X := StrToInt(Trim(vleObjectProperty.Values[MsgPropX]));
5209 Y := StrToInt(Trim(vleObjectProperty.Values[MsgPropY]));
5210 OnlyDM := NameToBool(vleObjectProperty.Values[MsgPropDmOnly]);
5211 Fall := NameToBool(vleObjectProperty.Values[MsgPropItemFalls]);
5212 end;
5213 end;
5215 OBJECT_MONSTER:
5216 begin
5217 with gMonsters[SelectedObjects[_id].ID] do
5218 begin
5219 X := StrToInt(Trim(vleObjectProperty.Values[MsgPropX]));
5220 Y := StrToInt(Trim(vleObjectProperty.Values[MsgPropY]));
5221 Direction := NameToDir(vleObjectProperty.Values[MsgPropDirection]);
5222 end;
5223 end;
5225 OBJECT_AREA:
5226 begin
5227 with gAreas[SelectedObjects[_id].ID] do
5228 begin
5229 X := StrToInt(Trim(vleObjectProperty.Values[MsgPropX]));
5230 Y := StrToInt(Trim(vleObjectProperty.Values[MsgPropY]));
5231 Direction := NameToDir(vleObjectProperty.Values[MsgPropDirection]);
5232 end;
5233 end;
5235 OBJECT_TRIGGER:
5236 begin
5237 with gTriggers[SelectedObjects[_id].ID] do
5238 begin
5239 X := StrToInt(Trim(vleObjectProperty.Values[MsgPropX]));
5240 Y := StrToInt(Trim(vleObjectProperty.Values[MsgPropY]));
5241 Width := StrToInt(Trim(vleObjectProperty.Values[MsgPropWidth]));
5242 Height := StrToInt(Trim(vleObjectProperty.Values[MsgPropHeight]));
5243 Enabled := NameToBool(vleObjectProperty.Values[MsgPropTrEnabled]);
5244 ActivateType := StrToActivate(vleObjectProperty.Values[MsgPropTrActivation]);
5245 Key := StrToKey(vleObjectProperty.Values[MsgPropTrKeys]);
5247 case TriggerType of
5248 TRIGGER_EXIT:
5249 begin
5250 s := utf2win(vleObjectProperty.Values[MsgPropTrNextMap]);
5251 FillByte(Data.MapName[0], 16, 0);
5252 if s <> '' then
5253 Move(s[1], Data.MapName[0], Min(Length(s), 16));
5254 end;
5256 TRIGGER_TEXTURE:
5257 begin
5258 Data.ActivateOnce := NameToBool(vleObjectProperty.Values[MsgPropTrTextureOnce]);
5259 Data.AnimOnce := NameToBool(vleObjectProperty.Values[MsgPropTrTextureAnimOnce]);
5260 end;
5262 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
5263 begin
5264 Data.Wait := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrExDelay], 0), 65535);
5265 Data.Count := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrExCount], 0), 65535);
5266 if Data.Count < 1 then
5267 Data.Count := 1;
5268 if TriggerType = TRIGGER_PRESS then
5269 Data.ExtRandom := NameToBool(vleObjectProperty.Values[MsgPropTrExRandom]);
5270 end;
5272 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
5273 TRIGGER_CLOSETRAP, TRIGGER_TRAP, TRIGGER_LIFTUP, TRIGGER_LIFTDOWN,
5274 TRIGGER_LIFT:
5275 begin
5276 Data.NoSound := NameToBool(vleObjectProperty.Values[MsgPropTrSilent]);
5277 Data.d2d_doors := NameToBool(vleObjectProperty.Values[MsgPropTrD2d]);
5278 end;
5280 TRIGGER_TELEPORT:
5281 begin
5282 Data.d2d_teleport := NameToBool(vleObjectProperty.Values[MsgPropTrD2d]);
5283 Data.silent_teleport := NameToBool(vleObjectProperty.Values[MsgPropTrTeleportSilent]);
5284 Data.TlpDir := NameToDirAdv(vleObjectProperty.Values[MsgPropTrTeleportDir]);
5285 end;
5287 TRIGGER_SOUND:
5288 begin
5289 s := utf2win(vleObjectProperty.Values[MsgPropTrSoundName]);
5290 FillByte(Data.SoundName[0], 64, 0);
5291 if s <> '' then
5292 Move(s[1], Data.SoundName[0], Min(Length(s), 64));
5294 Data.Volume := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrSoundVolume], 0), 255);
5295 Data.Pan := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrSoundPan], 0), 255);
5296 Data.PlayCount := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrSoundCount], 0), 255);
5297 Data.Local := NameToBool(vleObjectProperty.Values[MsgPropTrSoundLocal]);
5298 Data.SoundSwitch := NameToBool(vleObjectProperty.Values[MsgPropTrSoundSwitch]);
5299 end;
5301 TRIGGER_SPAWNMONSTER:
5302 begin
5303 Data.MonType := StrToMonster(vleObjectProperty.Values[MsgPropTrMonsterType]);
5304 Data.MonDir := Byte(NameToDir(vleObjectProperty.Values[MsgPropDirection]));
5305 Data.MonHealth := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrHealth], 0), 1000000);
5306 if Data.MonHealth < 0 then
5307 Data.MonHealth := 0;
5308 Data.MonActive := NameToBool(vleObjectProperty.Values[MsgPropTrMonsterActive]);
5309 Data.MonCount := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrCount], 0), 64);
5310 if Data.MonCount < 1 then
5311 Data.MonCount := 1;
5312 Data.MonEffect := StrToEffect(vleObjectProperty.Values[MsgPropTrFxType]);
5313 Data.MonMax := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrSpawnMax], 0), 65535);
5314 Data.MonDelay := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrSpawnDelay], 0), 65535);
5315 Data.MonBehav := 0;
5316 if vleObjectProperty.Values[MsgPropTrMonsterBehaviour] = MsgPropTrMonsterBehaviour1 then
5317 Data.MonBehav := 1;
5318 if vleObjectProperty.Values[MsgPropTrMonsterBehaviour] = MsgPropTrMonsterBehaviour2 then
5319 Data.MonBehav := 2;
5320 if vleObjectProperty.Values[MsgPropTrMonsterBehaviour] = MsgPropTrMonsterBehaviour3 then
5321 Data.MonBehav := 3;
5322 if vleObjectProperty.Values[MsgPropTrMonsterBehaviour] = MsgPropTrMonsterBehaviour4 then
5323 Data.MonBehav := 4;
5324 if vleObjectProperty.Values[MsgPropTrMonsterBehaviour] = MsgPropTrMonsterBehaviour5 then
5325 Data.MonBehav := 5;
5326 end;
5328 TRIGGER_SPAWNITEM:
5329 begin
5330 Data.ItemType := StrToItem(vleObjectProperty.Values[MsgPropTrItemType]);
5331 Data.ItemOnlyDM := NameToBool(vleObjectProperty.Values[MsgPropDmOnly]);
5332 Data.ItemFalls := NameToBool(vleObjectProperty.Values[MsgPropItemFalls]);
5333 Data.ItemCount := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrCount], 0), 64);
5334 if Data.ItemCount < 1 then
5335 Data.ItemCount := 1;
5336 Data.ItemEffect := StrToEffect(vleObjectProperty.Values[MsgPropTrFxType]);
5337 Data.ItemMax := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrSpawnMax], 0), 65535);
5338 Data.ItemDelay := Min(StrToIntDef(vleObjectProperty.Values[MsgPropTrSpawnDelay], 0), 65535);
5339 end;
5341 TRIGGER_MUSIC:
5342 begin
5343 s := utf2win(vleObjectProperty.Values[MsgPropTrMusicName]);
5344 FillByte(Data.MusicName[0], 64, 0);
5345 if s <> '' then
5346 Move(s[1], Data.MusicName[0], Min(Length(s), 64));
5348 if vleObjectProperty.Values[MsgPropTrMusicAct] = MsgPropTrMusicOn then
5349 Data.MusicAction := 1
5350 else
5351 Data.MusicAction := 0;
5352 end;
5354 TRIGGER_PUSH:
5355 begin
5356 Data.PushAngle := Min(
5357 StrToIntDef(vleObjectProperty.Values[MsgPropTrPushAngle], 0), 360);
5358 Data.PushForce := Min(
5359 StrToIntDef(vleObjectProperty.Values[MsgPropTrPushForce], 0), 255);
5360 Data.ResetVel := NameToBool(vleObjectProperty.Values[MsgPropTrPushReset]);
5361 end;
5363 TRIGGER_SCORE:
5364 begin
5365 Data.ScoreAction := 0;
5366 if vleObjectProperty.Values[MsgPropTrScoreAct] = MsgPropTrScoreAct1 then
5367 Data.ScoreAction := 1
5368 else if vleObjectProperty.Values[MsgPropTrScoreAct] = MsgPropTrScoreAct2 then
5369 Data.ScoreAction := 2
5370 else if vleObjectProperty.Values[MsgPropTrScoreAct] = MsgPropTrScoreAct3 then
5371 Data.ScoreAction := 3;
5372 Data.ScoreCount := Min(Max(
5373 StrToIntDef(vleObjectProperty.Values[MsgPropTrCount], 0), 0), 255);
5374 Data.ScoreTeam := 0;
5375 if vleObjectProperty.Values[MsgPropTrScoreTeam] = MsgPropTrScoreTeam1 then
5376 Data.ScoreTeam := 1
5377 else if vleObjectProperty.Values[MsgPropTrScoreTeam] = MsgPropTrScoreTeam2 then
5378 Data.ScoreTeam := 2
5379 else if vleObjectProperty.Values[MsgPropTrScoreTeam] = MsgPropTrScoreTeam3 then
5380 Data.ScoreTeam := 3;
5381 Data.ScoreCon := NameToBool(vleObjectProperty.Values[MsgPropTrScoreCon]);
5382 Data.ScoreMsg := NameToBool(vleObjectProperty.Values[MsgPropTrScoreMsg]);
5383 end;
5385 TRIGGER_MESSAGE:
5386 begin
5387 Data.MessageKind := 0;
5388 if vleObjectProperty.Values[MsgPropTrMessageKind] = MsgPropTrMessageKind1 then
5389 Data.MessageKind := 1;
5391 Data.MessageSendTo := 0;
5392 if vleObjectProperty.Values[MsgPropTrMessageTo] = MsgPropTrMessageTo1 then
5393 Data.MessageSendTo := 1
5394 else if vleObjectProperty.Values[MsgPropTrMessageTo] = MsgPropTrMessageTo2 then
5395 Data.MessageSendTo := 2
5396 else if vleObjectProperty.Values[MsgPropTrMessageTo] = MsgPropTrMessageTo3 then
5397 Data.MessageSendTo := 3
5398 else if vleObjectProperty.Values[MsgPropTrMessageTo] = MsgPropTrMessageTo4 then
5399 Data.MessageSendTo := 4
5400 else if vleObjectProperty.Values[MsgPropTrMessageTo] = MsgPropTrMessageTo5 then
5401 Data.MessageSendTo := 5;
5403 s := utf2win(vleObjectProperty.Values[MsgPropTrMessageText]);
5404 FillByte(Data.MessageText[0], 100, 0);
5405 if s <> '' then
5406 Move(s[1], Data.MessageText[0], Min(Length(s), 100));
5408 Data.MessageTime := Min(Max(
5409 StrToIntDef(vleObjectProperty.Values[MsgPropTrMessageTime], 0), 0), 65535);
5410 end;
5412 TRIGGER_DAMAGE:
5413 begin
5414 Data.DamageValue := Min(Max(
5415 StrToIntDef(vleObjectProperty.Values[MsgPropTrDamageValue], 0), 0), 65535);
5416 Data.DamageInterval := Min(Max(
5417 StrToIntDef(vleObjectProperty.Values[MsgPropTrInterval], 0), 0), 65535);
5418 s := vleObjectProperty.Values[MsgPropTrDamageKind];
5419 if s = MsgPropTrDamageKind3 then
5420 Data.DamageKind := 3
5421 else if s = MsgPropTrDamageKind4 then
5422 Data.DamageKind := 4
5423 else if s = MsgPropTrDamageKind5 then
5424 Data.DamageKind := 5
5425 else if s = MsgPropTrDamageKind6 then
5426 Data.DamageKind := 6
5427 else if s = MsgPropTrDamageKind7 then
5428 Data.DamageKind := 7
5429 else if s = MsgPropTrDamageKind8 then
5430 Data.DamageKind := 8
5431 else
5432 Data.DamageKind := 0;
5433 end;
5435 TRIGGER_HEALTH:
5436 begin
5437 Data.HealValue := Min(Max(
5438 StrToIntDef(vleObjectProperty.Values[MsgPropTrHealth], 0), 0), 65535);
5439 Data.HealInterval := Min(Max(
5440 StrToIntDef(vleObjectProperty.Values[MsgPropTrInterval], 0), 0), 65535);
5441 Data.HealMax := NameToBool(vleObjectProperty.Values[MsgPropTrHealthMax]);
5442 Data.HealSilent := NameToBool(vleObjectProperty.Values[MsgPropTrSilent]);
5443 end;
5445 TRIGGER_SHOT:
5446 begin
5447 Data.ShotType := StrToShot(vleObjectProperty.Values[MsgPropTrShotType]);
5448 Data.ShotSound := NameToBool(vleObjectProperty.Values[MsgPropTrShotSound]);
5449 Data.ShotTarget := 0;
5450 if vleObjectProperty.Values[MsgPropTrShotTo] = MsgPropTrShotTo1 then
5451 Data.ShotTarget := 1
5452 else if vleObjectProperty.Values[MsgPropTrShotTo] = MsgPropTrShotTo2 then
5453 Data.ShotTarget := 2
5454 else if vleObjectProperty.Values[MsgPropTrShotTo] = MsgPropTrShotTo3 then
5455 Data.ShotTarget := 3
5456 else if vleObjectProperty.Values[MsgPropTrShotTo] = MsgPropTrShotTo4 then
5457 Data.ShotTarget := 4
5458 else if vleObjectProperty.Values[MsgPropTrShotTo] = MsgPropTrShotTo5 then
5459 Data.ShotTarget := 5
5460 else if vleObjectProperty.Values[MsgPropTrShotTo] = MsgPropTrShotTo6 then
5461 Data.ShotTarget := 6;
5462 Data.ShotIntSight := Min(Max(
5463 StrToIntDef(vleObjectProperty.Values[MsgPropTrShotSight], 0), 0), 65535);
5464 Data.ShotAim := 0;
5465 if vleObjectProperty.Values[MsgPropTrShotAim] = MsgPropTrShotAim1 then
5466 Data.ShotAim := 1
5467 else if vleObjectProperty.Values[MsgPropTrShotAim] = MsgPropTrShotAim2 then
5468 Data.ShotAim := 2
5469 else if vleObjectProperty.Values[MsgPropTrShotAim] = MsgPropTrShotAim3 then
5470 Data.ShotAim := 3;
5471 Data.ShotAngle := Min(
5472 StrToIntDef(vleObjectProperty.Values[MsgPropTrShotAngle], 0), 360);
5473 Data.ShotWait := Min(Max(
5474 StrToIntDef(vleObjectProperty.Values[MsgPropTrExDelay], 0), 0), 65535);
5475 Data.ShotAccuracy := Min(Max(
5476 StrToIntDef(vleObjectProperty.Values[MsgPropTrShotAcc], 0), 0), 65535);
5477 Data.ShotAmmo := Min(Max(
5478 StrToIntDef(vleObjectProperty.Values[MsgPropTrShotAmmo], 0), 0), 65535);
5479 Data.ShotIntReload := Min(Max(
5480 StrToIntDef(vleObjectProperty.Values[MsgPropTrShotReload], 0), 0), 65535);
5481 end;
5483 TRIGGER_EFFECT:
5484 begin
5485 Data.FXCount := Min(Max(
5486 StrToIntDef(vleObjectProperty.Values[MsgPropTrCount], 0), 0), 255);
5487 if vleObjectProperty.Values[MsgPropTrEffectType] = MsgPropTrEffectParticle then
5488 begin
5489 Data.FXType := TRIGGER_EFFECT_PARTICLE;
5490 Data.FXSubType := TRIGGER_EFFECT_SLIQUID;
5491 if vleObjectProperty.Values[MsgPropTrEffectSubtype] = MsgPropTrEffectSliquid then
5492 Data.FXSubType := TRIGGER_EFFECT_SLIQUID
5493 else if vleObjectProperty.Values[MsgPropTrEffectSubtype] = MsgPropTrEffectLliquid then
5494 Data.FXSubType := TRIGGER_EFFECT_LLIQUID
5495 else if vleObjectProperty.Values[MsgPropTrEffectSubtype] = MsgPropTrEffectDliquid then
5496 Data.FXSubType := TRIGGER_EFFECT_DLIQUID
5497 else if vleObjectProperty.Values[MsgPropTrEffectSubtype] = MsgPropTrEffectBlood then
5498 Data.FXSubType := TRIGGER_EFFECT_BLOOD
5499 else if vleObjectProperty.Values[MsgPropTrEffectSubtype] = MsgPropTrEffectSpark then
5500 Data.FXSubType := TRIGGER_EFFECT_SPARK
5501 else if vleObjectProperty.Values[MsgPropTrEffectSubtype] = MsgPropTrEffectBubble then
5502 Data.FXSubType := TRIGGER_EFFECT_BUBBLE;
5503 end else
5504 begin
5505 Data.FXType := TRIGGER_EFFECT_ANIMATION;
5506 Data.FXSubType := StrToEffect(vleObjectProperty.Values[MsgPropTrEffectSubtype]);
5507 end;
5508 a := Min(Max(
5509 StrToIntDef(vleObjectProperty.Values[MsgPropTrEffectColor], 0), 0), $FFFFFF);
5510 Data.FXColorR := a and $FF;
5511 Data.FXColorG := (a shr 8) and $FF;
5512 Data.FXColorB := (a shr 16) and $FF;
5513 if NameToBool(vleObjectProperty.Values[MsgPropTrEffectCenter]) then
5514 Data.FXPos := 0
5515 else
5516 Data.FXPos := 1;
5517 Data.FXWait := Min(Max(
5518 StrToIntDef(vleObjectProperty.Values[MsgPropTrExDelay], 0), 0), 65535);
5519 Data.FXVelX := Min(Max(
5520 StrToIntDef(vleObjectProperty.Values[MsgPropTrEffectVelx], 0), -128), 127);
5521 Data.FXVelY := Min(Max(
5522 StrToIntDef(vleObjectProperty.Values[MsgPropTrEffectVely], 0), -128), 127);
5523 Data.FXSpreadL := Min(Max(
5524 StrToIntDef(vleObjectProperty.Values[MsgPropTrEffectSpl], 0), 0), 255);
5525 Data.FXSpreadR := Min(Max(
5526 StrToIntDef(vleObjectProperty.Values[MsgPropTrEffectSpr], 0), 0), 255);
5527 Data.FXSpreadU := Min(Max(
5528 StrToIntDef(vleObjectProperty.Values[MsgPropTrEffectSpu], 0), 0), 255);
5529 Data.FXSpreadD := Min(Max(
5530 StrToIntDef(vleObjectProperty.Values[MsgPropTrEffectSpd], 0), 0), 255);
5531 end;
5532 end;
5533 end;
5534 end;
5535 end;
5537 FillProperty();
5539 vleObjectProperty.Row := r;
5540 vleObjectProperty.Col := c;
5541 end;
5543 procedure TMainForm.bbRemoveTextureClick(Sender: TObject);
5544 var
5545 a, i: Integer;
5546 begin
5547 i := lbTextureList.ItemIndex;
5548 if i = -1 then
5549 Exit;
5551 if Application.MessageBox(PChar(Format(MsgMsgDelTexturePromt,
5552 [SelectedTexture()])),
5553 PChar(MsgMsgDelTexture),
5554 MB_ICONQUESTION or MB_YESNO or
5555 MB_DEFBUTTON1) <> idYes then
5556 Exit;
5558 if gPanels <> nil then
5559 for a := 0 to High(gPanels) do
5560 if (gPanels[a].PanelType <> 0) and
5561 (gPanels[a].TextureName = SelectedTexture()) then
5562 begin
5563 ErrorMessageBox(MsgMsgDelTextureCant);
5564 Exit;
5565 end;
5567 g_DeleteTexture(SelectedTexture());
5568 i := slInvalidTextures.IndexOf(lbTextureList.Items[i]);
5569 if i > -1 then
5570 slInvalidTextures.Delete(i);
5571 if lbTextureList.ItemIndex > -1 then
5572 lbTextureList.Items.Delete(lbTextureList.ItemIndex)
5573 end;
5575 procedure TMainForm.aNewMapExecute(Sender: TObject);
5576 begin
5577 if Application.MessageBox(PChar(MsgMsgClearMapPromt), PChar(MsgMsgClearMap), MB_ICONQUESTION or MB_YESNO or MB_DEFBUTTON1) = mrYes then
5578 FullClear();
5579 end;
5581 procedure TMainForm.aUndoExecute(Sender: TObject);
5582 var
5583 a: Integer;
5584 begin
5585 if UndoBuffer = nil then
5586 Exit;
5587 if UndoBuffer[High(UndoBuffer)] = nil then
5588 Exit;
5590 for a := 0 to High(UndoBuffer[High(UndoBuffer)]) do
5591 with UndoBuffer[High(UndoBuffer)][a] do
5592 begin
5593 case UndoType of
5594 UNDO_DELETE_PANEL:
5595 begin
5596 AddPanel(Panel^);
5597 Panel := nil;
5598 end;
5599 UNDO_DELETE_ITEM: AddItem(Item);
5600 UNDO_DELETE_AREA: AddArea(Area);
5601 UNDO_DELETE_MONSTER: AddMonster(Monster);
5602 UNDO_DELETE_TRIGGER: AddTrigger(Trigger);
5603 UNDO_ADD_PANEL: RemoveObject(AddID, OBJECT_PANEL);
5604 UNDO_ADD_ITEM: RemoveObject(AddID, OBJECT_ITEM);
5605 UNDO_ADD_AREA: RemoveObject(AddID, OBJECT_AREA);
5606 UNDO_ADD_MONSTER: RemoveObject(AddID, OBJECT_MONSTER);
5607 UNDO_ADD_TRIGGER: RemoveObject(AddID, OBJECT_TRIGGER);
5608 end;
5609 end;
5611 SetLength(UndoBuffer, Length(UndoBuffer)-1);
5613 RemoveSelectFromObjects();
5615 miUndo.Enabled := UndoBuffer <> nil;
5616 end;
5619 procedure TMainForm.aCopyObjectExecute(Sender: TObject);
5620 var
5621 a, b: Integer;
5622 CopyBuffer: TCopyRecArray;
5623 str: String;
5624 ok: Boolean;
5626 function CB_Compare(I1, I2: TCopyRec): Integer;
5627 begin
5628 Result := Integer(I1.ObjectType) - Integer(I2.ObjectType);
5630 if Result = 0 then // Одного типа
5631 Result := Integer(I1.ID) - Integer(I2.ID);
5632 end;
5634 procedure QuickSortCopyBuffer(L, R: Integer);
5635 var
5636 I, J: Integer;
5637 P, T: TCopyRec;
5638 begin
5639 repeat
5640 I := L;
5641 J := R;
5642 P := CopyBuffer[(L + R) shr 1];
5644 repeat
5645 while CB_Compare(CopyBuffer[I], P) < 0 do
5646 Inc(I);
5647 while CB_Compare(CopyBuffer[J], P) > 0 do
5648 Dec(J);
5650 if I <= J then
5651 begin
5652 T := CopyBuffer[I];
5653 CopyBuffer[I] := CopyBuffer[J];
5654 CopyBuffer[J] := T;
5655 Inc(I);
5656 Dec(J);
5657 end;
5658 until I > J;
5660 if L < J then
5661 QuickSortCopyBuffer(L, J);
5663 L := I;
5664 until I >= R;
5665 end;
5667 begin
5668 if SelectedObjects = nil then
5669 Exit;
5671 b := -1;
5672 CopyBuffer := nil;
5674 // Копируем объекты:
5675 for a := 0 to High(SelectedObjects) do
5676 if SelectedObjects[a].Live then
5677 with SelectedObjects[a] do
5678 begin
5679 SetLength(CopyBuffer, Length(CopyBuffer)+1);
5680 b := High(CopyBuffer);
5681 CopyBuffer[b].ID := ID;
5682 CopyBuffer[b].Panel := nil;
5684 case ObjectType of
5685 OBJECT_PANEL:
5686 begin
5687 CopyBuffer[b].ObjectType := OBJECT_PANEL;
5688 New(CopyBuffer[b].Panel);
5689 CopyBuffer[b].Panel^ := gPanels[ID];
5690 end;
5692 OBJECT_ITEM:
5693 begin
5694 CopyBuffer[b].ObjectType := OBJECT_ITEM;
5695 CopyBuffer[b].Item := gItems[ID];
5696 end;
5698 OBJECT_MONSTER:
5699 begin
5700 CopyBuffer[b].ObjectType := OBJECT_MONSTER;
5701 CopyBuffer[b].Monster := gMonsters[ID];
5702 end;
5704 OBJECT_AREA:
5705 begin
5706 CopyBuffer[b].ObjectType := OBJECT_AREA;
5707 CopyBuffer[b].Area := gAreas[ID];
5708 end;
5710 OBJECT_TRIGGER:
5711 begin
5712 CopyBuffer[b].ObjectType := OBJECT_TRIGGER;
5713 CopyBuffer[b].Trigger := gTriggers[ID];
5714 end;
5715 end;
5716 end;
5718 // Сортировка по ID:
5719 if CopyBuffer <> nil then
5720 begin
5721 QuickSortCopyBuffer(0, b);
5722 end;
5724 // Пестановка ссылок триггеров:
5725 for a := 0 to Length(CopyBuffer)-1 do
5726 if CopyBuffer[a].ObjectType = OBJECT_TRIGGER then
5727 begin
5728 case CopyBuffer[a].Trigger.TriggerType of
5729 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
5730 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
5731 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
5732 if CopyBuffer[a].Trigger.Data.PanelID <> -1 then
5733 begin
5734 ok := False;
5736 for b := 0 to Length(CopyBuffer)-1 do
5737 if (CopyBuffer[b].ObjectType = OBJECT_PANEL) and
5738 (Integer(CopyBuffer[b].ID) = CopyBuffer[a].Trigger.Data.PanelID) then
5739 begin
5740 CopyBuffer[a].Trigger.Data.PanelID := b;
5741 ok := True;
5742 Break;
5743 end;
5745 // Этих панелей нет среди копируемых:
5746 if not ok then
5747 CopyBuffer[a].Trigger.Data.PanelID := -1;
5748 end;
5750 TRIGGER_PRESS, TRIGGER_ON,
5751 TRIGGER_OFF, TRIGGER_ONOFF:
5752 if CopyBuffer[a].Trigger.Data.MonsterID <> 0 then
5753 begin
5754 ok := False;
5756 for b := 0 to Length(CopyBuffer)-1 do
5757 if (CopyBuffer[b].ObjectType = OBJECT_MONSTER) and
5758 (Integer(CopyBuffer[b].ID) = CopyBuffer[a].Trigger.Data.MonsterID-1) then
5759 begin
5760 CopyBuffer[a].Trigger.Data.MonsterID := b+1;
5761 ok := True;
5762 Break;
5763 end;
5765 // Этих монстров нет среди копируемых:
5766 if not ok then
5767 CopyBuffer[a].Trigger.Data.MonsterID := 0;
5768 end;
5770 TRIGGER_SHOT:
5771 if CopyBuffer[a].Trigger.Data.ShotPanelID <> -1 then
5772 begin
5773 ok := False;
5775 for b := 0 to Length(CopyBuffer)-1 do
5776 if (CopyBuffer[b].ObjectType = OBJECT_PANEL) and
5777 (Integer(CopyBuffer[b].ID) = CopyBuffer[a].Trigger.Data.ShotPanelID) then
5778 begin
5779 CopyBuffer[a].Trigger.Data.ShotPanelID := b;
5780 ok := True;
5781 Break;
5782 end;
5784 // Этих панелей нет среди копируемых:
5785 if not ok then
5786 CopyBuffer[a].Trigger.Data.ShotPanelID := -1;
5787 end;
5788 end;
5790 if CopyBuffer[a].Trigger.TexturePanel <> -1 then
5791 begin
5792 ok := False;
5794 for b := 0 to Length(CopyBuffer)-1 do
5795 if (CopyBuffer[b].ObjectType = OBJECT_PANEL) and
5796 (Integer(CopyBuffer[b].ID) = CopyBuffer[a].Trigger.TexturePanel) then
5797 begin
5798 CopyBuffer[a].Trigger.TexturePanel := b;
5799 ok := True;
5800 Break;
5801 end;
5803 // Этих панелей нет среди копируемых:
5804 if not ok then
5805 CopyBuffer[a].Trigger.TexturePanel := -1;
5806 end;
5807 end;
5809 // В буфер обмена:
5810 str := CopyBufferToString(CopyBuffer);
5811 ClipBoard.AsText := str;
5813 for a := 0 to Length(CopyBuffer)-1 do
5814 if (CopyBuffer[a].ObjectType = OBJECT_PANEL) and
5815 (CopyBuffer[a].Panel <> nil) then
5816 Dispose(CopyBuffer[a].Panel);
5818 CopyBuffer := nil;
5819 end;
5821 procedure TMainForm.aPasteObjectExecute(Sender: TObject);
5822 var
5823 a, h: Integer;
5824 CopyBuffer: TCopyRecArray;
5825 res, rel: Boolean;
5826 swad, ssec, sres: String;
5827 NoTextureID: DWORD;
5828 pmin: TPoint;
5829 begin
5830 CopyBuffer := nil;
5831 NoTextureID := 0;
5832 pmin.X := High(pmin.X);
5833 pmin.Y := High(pmin.Y);
5835 StringToCopyBuffer(ClipBoard.AsText, CopyBuffer, pmin);
5836 rel := not(ssShift in GetKeyShiftState());
5838 if CopyBuffer = nil then
5839 Exit;
5841 RemoveSelectFromObjects();
5843 h := High(CopyBuffer);
5844 for a := 0 to h do
5845 with CopyBuffer[a] do
5846 begin
5847 case ObjectType of
5848 OBJECT_PANEL:
5849 if Panel <> nil then
5850 begin
5851 if rel then
5852 begin
5853 Panel^.X := Panel^.X - pmin.X - MapOffset.X + 32;
5854 Panel^.Y := Panel^.Y - pmin.Y - MapOffset.Y + 32;
5855 end;
5857 Panel^.TextureID := TEXTURE_SPECIAL_NONE;
5858 Panel^.TextureWidth := 1;
5859 Panel^.TextureHeight := 1;
5861 if (Panel^.PanelType = PANEL_LIFTUP) or
5862 (Panel^.PanelType = PANEL_LIFTDOWN) or
5863 (Panel^.PanelType = PANEL_LIFTLEFT) or
5864 (Panel^.PanelType = PANEL_LIFTRIGHT) or
5865 (Panel^.PanelType = PANEL_BLOCKMON) or
5866 (Panel^.TextureName = '') then
5867 begin // Нет или не может быть текстуры:
5868 end
5869 else // Есть текстура:
5870 begin
5871 // Обычная текстура:
5872 if not IsSpecialTexture(Panel^.TextureName) then
5873 begin
5874 res := g_GetTexture(Panel^.TextureName, Panel^.TextureID);
5876 if not res then
5877 begin
5878 g_ProcessResourceStr(Panel^.TextureName, swad, ssec, sres);
5879 AddTexture(swad, ssec, sres, True);
5880 res := g_GetTexture(Panel^.TextureName, Panel^.TextureID);
5881 end;
5883 if res then
5884 g_GetTextureSizeByName(Panel^.TextureName,
5885 Panel^.TextureWidth, Panel^.TextureHeight)
5886 else
5887 if g_GetTexture('NOTEXTURE', NoTextureID) then
5888 begin
5889 Panel^.TextureID := TEXTURE_SPECIAL_NOTEXTURE;
5890 g_GetTextureSizeByID(NoTextureID, Panel^.TextureWidth, Panel^.TextureHeight);
5891 end;
5892 end
5893 else // Спец.текстура:
5894 begin
5895 Panel^.TextureID := SpecialTextureID(Panel^.TextureName);
5896 with MainForm.lbTextureList.Items do
5897 if IndexOf(Panel^.TextureName) = -1 then
5898 Add(Panel^.TextureName);
5899 end;
5900 end;
5902 ID := AddPanel(Panel^);
5903 Dispose(Panel);
5904 Undo_Add(OBJECT_PANEL, ID, a > 0);
5905 SelectObject(OBJECT_PANEL, ID, True);
5906 end;
5908 OBJECT_ITEM:
5909 begin
5910 if rel then
5911 begin
5912 Item.X := Item.X - pmin.X - MapOffset.X + 32;
5913 Item.Y := Item.Y - pmin.Y - MapOffset.Y + 32;
5914 end;
5916 ID := AddItem(Item);
5917 Undo_Add(OBJECT_ITEM, ID, a > 0);
5918 SelectObject(OBJECT_ITEM, ID, True);
5919 end;
5921 OBJECT_MONSTER:
5922 begin
5923 if rel then
5924 begin
5925 Monster.X := Monster.X - pmin.X - MapOffset.X + 32;
5926 Monster.Y := Monster.Y - pmin.Y - MapOffset.Y + 32;
5927 end;
5929 ID := AddMonster(Monster);
5930 Undo_Add(OBJECT_MONSTER, ID, a > 0);
5931 SelectObject(OBJECT_MONSTER, ID, True);
5932 end;
5934 OBJECT_AREA:
5935 begin
5936 if rel then
5937 begin
5938 Area.X := Area.X - pmin.X - MapOffset.X + 32;
5939 Area.Y := Area.Y - pmin.Y - MapOffset.Y + 32;
5940 end;
5942 ID := AddArea(Area);
5943 Undo_Add(OBJECT_AREA, ID, a > 0);
5944 SelectObject(OBJECT_AREA, ID, True);
5945 end;
5947 OBJECT_TRIGGER:
5948 begin
5949 if rel then
5950 with Trigger do
5951 begin
5952 X := X - pmin.X - MapOffset.X + 32;
5953 Y := Y - pmin.Y - MapOffset.Y + 32;
5955 case TriggerType of
5956 TRIGGER_TELEPORT:
5957 begin
5958 Data.TargetPoint.X :=
5959 Data.TargetPoint.X - pmin.X - MapOffset.X + 32;
5960 Data.TargetPoint.Y :=
5961 Data.TargetPoint.Y - pmin.Y - MapOffset.Y + 32;
5962 end;
5963 TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF:
5964 begin
5965 Data.tX := Data.tX - pmin.X - MapOffset.X + 32;
5966 Data.tY := Data.tY - pmin.Y - MapOffset.Y + 32;
5967 end;
5968 TRIGGER_SPAWNMONSTER:
5969 begin
5970 Data.MonPos.X :=
5971 Data.MonPos.X - pmin.X - MapOffset.X + 32;
5972 Data.MonPos.Y :=
5973 Data.MonPos.Y - pmin.Y - MapOffset.Y + 32;
5974 end;
5975 TRIGGER_SPAWNITEM:
5976 begin
5977 Data.ItemPos.X :=
5978 Data.ItemPos.X - pmin.X - MapOffset.X + 32;
5979 Data.ItemPos.Y :=
5980 Data.ItemPos.Y - pmin.Y - MapOffset.Y + 32;
5981 end;
5982 TRIGGER_SHOT:
5983 begin
5984 Data.ShotPos.X :=
5985 Data.ShotPos.X - pmin.X - MapOffset.X + 32;
5986 Data.ShotPos.Y :=
5987 Data.ShotPos.Y - pmin.Y - MapOffset.Y + 32;
5988 end;
5989 end;
5990 end;
5992 ID := AddTrigger(Trigger);
5993 Undo_Add(OBJECT_TRIGGER, ID, a > 0);
5994 SelectObject(OBJECT_TRIGGER, ID, True);
5995 end;
5996 end;
5997 end;
5999 // Переставляем ссылки триггеров:
6000 for a := 0 to High(CopyBuffer) do
6001 if CopyBuffer[a].ObjectType = OBJECT_TRIGGER then
6002 begin
6003 case CopyBuffer[a].Trigger.TriggerType of
6004 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
6005 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
6006 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
6007 if CopyBuffer[a].Trigger.Data.PanelID <> -1 then
6008 gTriggers[CopyBuffer[a].ID].Data.PanelID :=
6009 CopyBuffer[CopyBuffer[a].Trigger.Data.PanelID].ID;
6011 TRIGGER_PRESS, TRIGGER_ON,
6012 TRIGGER_OFF, TRIGGER_ONOFF:
6013 if CopyBuffer[a].Trigger.Data.MonsterID <> 0 then
6014 gTriggers[CopyBuffer[a].ID].Data.MonsterID :=
6015 CopyBuffer[CopyBuffer[a].Trigger.Data.MonsterID-1].ID+1;
6017 TRIGGER_SHOT:
6018 if CopyBuffer[a].Trigger.Data.ShotPanelID <> -1 then
6019 gTriggers[CopyBuffer[a].ID].Data.ShotPanelID :=
6020 CopyBuffer[CopyBuffer[a].Trigger.Data.ShotPanelID].ID;
6021 end;
6023 if CopyBuffer[a].Trigger.TexturePanel <> -1 then
6024 gTriggers[CopyBuffer[a].ID].TexturePanel :=
6025 CopyBuffer[CopyBuffer[a].Trigger.TexturePanel].ID;
6026 end;
6028 CopyBuffer := nil;
6030 if h = 0 then
6031 FillProperty();
6032 end;
6034 procedure TMainForm.aCutObjectExecute(Sender: TObject);
6035 begin
6036 miCopy.Click();
6037 DeleteSelectedObjects();
6038 end;
6040 procedure TMainForm.vleObjectPropertyEditButtonClick(Sender: TObject);
6041 var
6042 Key, FileName: String;
6043 b: Byte;
6044 begin
6045 Key := vleObjectProperty.Keys[vleObjectProperty.Row];
6047 if Key = MsgPropPanelType then
6048 begin
6049 with ChooseTypeForm, vleObjectProperty do
6050 begin // Выбор типа панели:
6051 Caption := MsgPropPanelType;
6052 lbTypeSelect.Items.Clear();
6054 for b := 0 to High(PANELNAMES) do
6055 begin
6056 lbTypeSelect.Items.Add(PANELNAMES[b]);
6057 if Values[Key] = PANELNAMES[b] then
6058 lbTypeSelect.ItemIndex := b;
6059 end;
6061 if ShowModal() = mrOK then
6062 begin
6063 b := lbTypeSelect.ItemIndex;
6064 Values[Key] := PANELNAMES[b];
6065 vleObjectPropertyApply(Sender);
6066 end;
6067 end
6068 end
6069 else if Key = MsgPropTrTeleportTo then
6070 SelectFlag := SELECTFLAG_TELEPORT
6071 else if Key = MsgPropTrSpawnTo then
6072 SelectFlag := SELECTFLAG_SPAWNPOINT
6073 else if (Key = MsgPropTrDoorPanel) or
6074 (Key = MsgPropTrTrapPanel) then
6075 SelectFlag := SELECTFLAG_DOOR
6076 else if Key = MsgPropTrTexturePanel then
6077 begin
6078 DrawPressRect := False;
6079 SelectFlag := SELECTFLAG_TEXTURE;
6080 end
6081 else if Key = MsgPropTrShotPanel then
6082 SelectFlag := SELECTFLAG_SHOTPANEL
6083 else if Key = MsgPropTrLiftPanel then
6084 SelectFlag := SELECTFLAG_LIFT
6085 else if key = MsgPropTrExMonster then
6086 SelectFlag := SELECTFLAG_MONSTER
6087 else if Key = MsgPropTrExArea then
6088 begin
6089 SelectFlag := SELECTFLAG_NONE;
6090 DrawPressRect := True;
6091 end
6092 else if Key = MsgPropTrNextMap then
6093 begin // Выбор следующей карты:
6094 g_ProcessResourceStr(OpenedMap, @FileName, nil, nil);
6095 SelectMapForm.Caption := MsgCapSelect;
6096 SelectMapForm.GetMaps(FileName);
6098 if SelectMapForm.ShowModal() = mrOK then
6099 begin
6100 vleObjectProperty.Values[Key] := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
6101 vleObjectPropertyApply(Sender);
6102 end;
6103 end
6104 else if (Key = MsgPropTrSoundName) or
6105 (Key = MsgPropTrMusicName) then
6106 begin // Выбор файла звука/музыки:
6107 AddSoundForm.OKFunction := nil;
6108 AddSoundForm.lbResourcesList.MultiSelect := False;
6109 AddSoundForm.SetResource := vleObjectProperty.Values[Key];
6111 if (AddSoundForm.ShowModal() = mrOk) then
6112 begin
6113 vleObjectProperty.Values[Key] := AddSoundForm.ResourceName;
6114 vleObjectPropertyApply(Sender);
6115 end;
6116 end
6117 else if Key = MsgPropTrActivation then
6118 with ActivationTypeForm, vleObjectProperty do
6119 begin // Выбор типов активации:
6120 cbPlayerCollide.Checked := Pos('PC', Values[Key]) > 0;
6121 cbMonsterCollide.Checked := Pos('MC', Values[Key]) > 0;
6122 cbPlayerPress.Checked := Pos('PP', Values[Key]) > 0;
6123 cbMonsterPress.Checked := Pos('MP', Values[Key]) > 0;
6124 cbShot.Checked := Pos('SH', Values[Key]) > 0;
6125 cbNoMonster.Checked := Pos('NM', Values[Key]) > 0;
6127 if ShowModal() = mrOK then
6128 begin
6129 b := 0;
6130 if cbPlayerCollide.Checked then
6131 b := ACTIVATE_PLAYERCOLLIDE;
6132 if cbMonsterCollide.Checked then
6133 b := b or ACTIVATE_MONSTERCOLLIDE;
6134 if cbPlayerPress.Checked then
6135 b := b or ACTIVATE_PLAYERPRESS;
6136 if cbMonsterPress.Checked then
6137 b := b or ACTIVATE_MONSTERPRESS;
6138 if cbShot.Checked then
6139 b := b or ACTIVATE_SHOT;
6140 if cbNoMonster.Checked then
6141 b := b or ACTIVATE_NOMONSTER;
6143 Values[Key] := ActivateToStr(b);
6144 vleObjectPropertyApply(Sender);
6145 end;
6146 end
6147 else if Key = MsgPropTrKeys then
6148 with KeysForm, vleObjectProperty do
6149 begin // Выбор необходимых ключей:
6150 cbRedKey.Checked := Pos('RK', Values[Key]) > 0;
6151 cbGreenKey.Checked := Pos('GK', Values[Key]) > 0;
6152 cbBlueKey.Checked := Pos('BK', Values[Key]) > 0;
6153 cbRedTeam.Checked := Pos('RT', Values[Key]) > 0;
6154 cbBlueTeam.Checked := Pos('BT', Values[Key]) > 0;
6156 if ShowModal() = mrOK then
6157 begin
6158 b := 0;
6159 if cbRedKey.Checked then
6160 b := KEY_RED;
6161 if cbGreenKey.Checked then
6162 b := b or KEY_GREEN;
6163 if cbBlueKey.Checked then
6164 b := b or KEY_BLUE;
6165 if cbRedTeam.Checked then
6166 b := b or KEY_REDTEAM;
6167 if cbBlueTeam.Checked then
6168 b := b or KEY_BLUETEAM;
6170 Values[Key] := KeyToStr(b);
6171 vleObjectPropertyApply(Sender);
6172 end;
6173 end
6174 else if Key = MsgPropTrFxType then
6175 with ChooseTypeForm, vleObjectProperty do
6176 begin // Выбор типа эффекта:
6177 Caption := MsgCapFxType;
6178 lbTypeSelect.Items.Clear();
6180 for b := EFFECT_NONE to EFFECT_FIRE do
6181 lbTypeSelect.Items.Add(EffectToStr(b));
6183 lbTypeSelect.ItemIndex := StrToEffect(Values[Key]);
6185 if ShowModal() = mrOK then
6186 begin
6187 b := lbTypeSelect.ItemIndex;
6188 Values[Key] := EffectToStr(b);
6189 vleObjectPropertyApply(Sender);
6190 end;
6191 end
6192 else if Key = MsgPropTrMonsterType then
6193 with ChooseTypeForm, vleObjectProperty do
6194 begin // Выбор типа монстра:
6195 Caption := MsgCapMonsterType;
6196 lbTypeSelect.Items.Clear();
6198 for b := MONSTER_DEMON to MONSTER_MAN do
6199 lbTypeSelect.Items.Add(MonsterToStr(b));
6201 lbTypeSelect.ItemIndex := StrToMonster(Values[Key]) - MONSTER_DEMON;
6203 if ShowModal() = mrOK then
6204 begin
6205 b := lbTypeSelect.ItemIndex + MONSTER_DEMON;
6206 Values[Key] := MonsterToStr(b);
6207 vleObjectPropertyApply(Sender);
6208 end;
6209 end
6210 else if Key = MsgPropTrItemType then
6211 with ChooseTypeForm, vleObjectProperty do
6212 begin // Выбор типа предмета:
6213 Caption := MsgCapItemType;
6214 lbTypeSelect.Items.Clear();
6216 for b := ITEM_MEDKIT_SMALL to ITEM_KEY_BLUE do
6217 lbTypeSelect.Items.Add(ItemToStr(b));
6218 lbTypeSelect.Items.Add(ItemToStr(ITEM_BOTTLE));
6219 lbTypeSelect.Items.Add(ItemToStr(ITEM_HELMET));
6220 lbTypeSelect.Items.Add(ItemToStr(ITEM_JETPACK));
6221 lbTypeSelect.Items.Add(ItemToStr(ITEM_INVIS));
6222 lbTypeSelect.Items.Add(ItemToStr(ITEM_WEAPON_FLAMETHROWER));
6223 lbTypeSelect.Items.Add(ItemToStr(ITEM_AMMO_FUELCAN));
6225 b := StrToItem(Values[Key]);
6226 if b >= ITEM_BOTTLE then
6227 b := b - 2;
6228 lbTypeSelect.ItemIndex := b - ITEM_MEDKIT_SMALL;
6230 if ShowModal() = mrOK then
6231 begin
6232 b := lbTypeSelect.ItemIndex + ITEM_MEDKIT_SMALL;
6233 if b >= ITEM_WEAPON_KASTET then
6234 b := b + 2;
6235 Values[Key] := ItemToStr(b);
6236 vleObjectPropertyApply(Sender);
6237 end;
6238 end
6239 else if Key = MsgPropTrShotType then
6240 with ChooseTypeForm, vleObjectProperty do
6241 begin // Выбор типа предмета:
6242 Caption := MsgPropTrShotType;
6243 lbTypeSelect.Items.Clear();
6245 for b := TRIGGER_SHOT_PISTOL to TRIGGER_SHOT_MAX do
6246 lbTypeSelect.Items.Add(ShotToStr(b));
6248 lbTypeSelect.ItemIndex := StrToShot(Values[Key]);
6250 if ShowModal() = mrOK then
6251 begin
6252 b := lbTypeSelect.ItemIndex;
6253 Values[Key] := ShotToStr(b);
6254 vleObjectPropertyApply(Sender);
6255 end;
6256 end
6257 else if Key = MsgPropTrEffectType then
6258 with ChooseTypeForm, vleObjectProperty do
6259 begin // Выбор типа эффекта:
6260 Caption := MsgCapFxType;
6261 lbTypeSelect.Items.Clear();
6263 lbTypeSelect.Items.Add(MsgPropTrEffectParticle);
6264 lbTypeSelect.Items.Add(MsgPropTrEffectAnimation);
6265 if Values[Key] = MsgPropTrEffectAnimation then
6266 lbTypeSelect.ItemIndex := 1
6267 else
6268 lbTypeSelect.ItemIndex := 0;
6270 if ShowModal() = mrOK then
6271 begin
6272 b := lbTypeSelect.ItemIndex;
6273 if b = 0 then
6274 Values[Key] := MsgPropTrEffectParticle
6275 else
6276 Values[Key] := MsgPropTrEffectAnimation;
6277 vleObjectPropertyApply(Sender);
6278 end;
6279 end
6280 else if Key = MsgPropTrEffectSubtype then
6281 with ChooseTypeForm, vleObjectProperty do
6282 begin // Выбор подтипа эффекта:
6283 Caption := MsgCapFxType;
6284 lbTypeSelect.Items.Clear();
6286 if Values[MsgPropTrEffectType] = MsgPropTrEffectAnimation then
6287 begin
6288 for b := EFFECT_TELEPORT to EFFECT_FIRE do
6289 lbTypeSelect.Items.Add(EffectToStr(b));
6291 lbTypeSelect.ItemIndex := StrToEffect(Values[Key]) - 1;
6292 end else
6293 begin
6294 lbTypeSelect.Items.Add(MsgPropTrEffectSliquid);
6295 lbTypeSelect.Items.Add(MsgPropTrEffectLliquid);
6296 lbTypeSelect.Items.Add(MsgPropTrEffectDliquid);
6297 lbTypeSelect.Items.Add(MsgPropTrEffectBlood);
6298 lbTypeSelect.Items.Add(MsgPropTrEffectSpark);
6299 lbTypeSelect.Items.Add(MsgPropTrEffectBubble);
6300 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_SLIQUID;
6301 if Values[Key] = MsgPropTrEffectLliquid then
6302 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_LLIQUID;
6303 if Values[Key] = MsgPropTrEffectDliquid then
6304 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_DLIQUID;
6305 if Values[Key] = MsgPropTrEffectBlood then
6306 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_BLOOD;
6307 if Values[Key] = MsgPropTrEffectSpark then
6308 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_SPARK;
6309 if Values[Key] = MsgPropTrEffectBubble then
6310 lbTypeSelect.ItemIndex := TRIGGER_EFFECT_BUBBLE;
6311 end;
6313 if ShowModal() = mrOK then
6314 begin
6315 b := lbTypeSelect.ItemIndex;
6317 if Values[MsgPropTrEffectType] = MsgPropTrEffectAnimation then
6318 Values[Key] := EffectToStr(b + 1)
6319 else begin
6320 Values[Key] := MsgPropTrEffectSliquid;
6321 if b = TRIGGER_EFFECT_LLIQUID then
6322 Values[Key] := MsgPropTrEffectLliquid;
6323 if b = TRIGGER_EFFECT_DLIQUID then
6324 Values[Key] := MsgPropTrEffectDliquid;
6325 if b = TRIGGER_EFFECT_BLOOD then
6326 Values[Key] := MsgPropTrEffectBlood;
6327 if b = TRIGGER_EFFECT_SPARK then
6328 Values[Key] := MsgPropTrEffectSpark;
6329 if b = TRIGGER_EFFECT_BUBBLE then
6330 Values[Key] := MsgPropTrEffectBubble;
6331 end;
6333 vleObjectPropertyApply(Sender);
6334 end;
6335 end
6336 else if Key = MsgPropTrEffectColor then
6337 with vleObjectProperty do
6338 begin // Выбор цвета эффекта:
6339 ColorDialog.Color := StrToIntDef(Values[Key], 0);
6340 if ColorDialog.Execute then
6341 begin
6342 Values[Key] := IntToStr(ColorDialog.Color);
6343 vleObjectPropertyApply(Sender);
6344 end;
6345 end
6346 else if Key = MsgPropPanelTex then
6347 begin // Смена текстуры:
6348 vleObjectProperty.Values[Key] := SelectedTexture();
6349 vleObjectPropertyApply(Sender);
6350 end;
6351 end;
6353 procedure TMainForm.vleObjectPropertyApply(Sender: TObject);
6354 begin
6355 // hack to prevent empty ID in list
6356 RenderPanel.SetFocus();
6357 bApplyProperty.Click();
6358 vleObjectProperty.SetFocus();
6359 end;
6361 procedure TMainForm.aSaveMapExecute(Sender: TObject);
6362 var
6363 FileName, Section, Res: String;
6364 begin
6365 if OpenedMap = '' then
6366 begin
6367 aSaveMapAsExecute(nil);
6368 Exit;
6369 end;
6371 g_ProcessResourceStr(OpenedMap, FileName, Section, Res);
6373 SaveMap(FileName+':\'+Res);
6374 end;
6376 procedure TMainForm.aOpenMapExecute(Sender: TObject);
6377 begin
6378 OpenDialog.Filter := MsgFileFilterAll;
6380 if OpenDialog.Execute() then
6381 begin
6382 OpenMapFile(OpenDialog.FileName);
6383 OpenDialog.InitialDir := ExtractFileDir(OpenDialog.FileName);
6384 end;
6385 end;
6387 procedure TMainForm.OpenMapFile(FileName: String);
6388 begin
6389 if (Pos('.ini', LowerCase(ExtractFileName(FileName))) > 0) then
6390 begin // INI карты:
6391 FullClear();
6393 pLoadProgress.Left := (RenderPanel.Width div 2)-(pLoadProgress.Width div 2);
6394 pLoadProgress.Top := (RenderPanel.Height div 2)-(pLoadProgress.Height div 2);
6395 pLoadProgress.Show();
6397 OpenedMap := '';
6398 OpenedWAD := '';
6400 LoadMapOld(FileName);
6402 MainForm.Caption := Format('%s - %s', [FormCaption, ExtractFileName(FileName)]);
6404 pLoadProgress.Hide();
6405 MainForm.FormResize(Self);
6406 end
6407 else // Карты из WAD:
6408 begin
6409 OpenMap(FileName, '');
6410 end;
6411 end;
6413 procedure TMainForm.FormActivate(Sender: TObject);
6414 begin
6415 MainForm.ActiveControl := RenderPanel;
6416 end;
6418 procedure TMainForm.aDeleteMap(Sender: TObject);
6419 var
6420 res: Integer;
6421 FileName: String;
6422 MapName: String;
6423 begin
6424 OpenDialog.Filter := MsgFileFilterWad;
6426 if not OpenDialog.Execute() then
6427 Exit;
6429 FileName := OpenDialog.FileName;
6430 SelectMapForm.Caption := MsgCapRemove;
6431 SelectMapForm.lbMapList.Items.Clear();
6432 SelectMapForm.GetMaps(FileName);
6434 if SelectMapForm.ShowModal() <> mrOK then
6435 Exit;
6437 MapName := SelectMapForm.lbMapList.Items[SelectMapForm.lbMapList.ItemIndex];
6438 if Application.MessageBox(PChar(Format(MsgMsgDeleteMapPromt, [MapName, OpenDialog.FileName])), PChar(MsgMsgDeleteMap), MB_ICONQUESTION or MB_YESNO or MB_DEFBUTTON2) <> mrYes then
6439 Exit;
6441 g_DeleteResource(FileName, '', MapName, res);
6442 if res <> 0 then
6443 begin
6444 Application.MessageBox(PChar('Cant delete map res=' + IntToStr(res)), PChar('Map not deleted!'), MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1);
6445 Exit
6446 end;
6448 Application.MessageBox(
6449 PChar(Format(MsgMsgMapDeletedPromt, [MapName])),
6450 PChar(MsgMsgMapDeleted),
6451 MB_ICONINFORMATION or MB_OK or MB_DEFBUTTON1
6452 );
6454 // Удалили текущую карту - сохранять по старому ее нельзя:
6455 if OpenedMap = (FileName + ':\' + MapName) then
6456 begin
6457 OpenedMap := '';
6458 OpenedWAD := '';
6459 MainForm.Caption := FormCaption
6460 end
6461 end;
6463 procedure TMainForm.vleObjectPropertyKeyDown(Sender: TObject;
6464 var Key: Word; Shift: TShiftState);
6465 begin
6466 if Key = VK_RETURN then
6467 vleObjectPropertyApply(Sender);
6468 end;
6470 procedure MovePanel(var ID: DWORD; MoveType: Byte);
6471 var
6472 _id, a: Integer;
6473 tmp: TPanel;
6474 begin
6475 if (ID = 0) and (MoveType = 0) then
6476 Exit;
6477 if (ID = DWORD(High(gPanels))) and (MoveType <> 0) then
6478 Exit;
6479 if (ID > DWORD(High(gPanels))) then
6480 Exit;
6482 _id := Integer(ID);
6484 if MoveType = 0 then // to Back
6485 begin
6486 if gTriggers <> nil then
6487 for a := 0 to High(gTriggers) do
6488 with gTriggers[a] do
6489 begin
6490 if TriggerType = TRIGGER_NONE then
6491 Continue;
6493 if TexturePanel = _id then
6494 TexturePanel := 0
6495 else
6496 if (TexturePanel >= 0) and (TexturePanel < _id) then
6497 Inc(TexturePanel);
6499 case TriggerType of
6500 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
6501 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
6502 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
6503 if Data.PanelID = _id then
6504 Data.PanelID := 0
6505 else
6506 if (Data.PanelID >= 0) and (Data.PanelID < _id) then
6507 Inc(Data.PanelID);
6509 TRIGGER_SHOT:
6510 if Data.ShotPanelID = _id then
6511 Data.ShotPanelID := 0
6512 else
6513 if (Data.ShotPanelID >= 0) and (Data.ShotPanelID < _id) then
6514 Inc(Data.ShotPanelID);
6515 end;
6516 end;
6518 tmp := gPanels[_id];
6520 for a := _id downto 1 do
6521 gPanels[a] := gPanels[a-1];
6523 gPanels[0] := tmp;
6525 ID := 0;
6526 end
6527 else // to Front
6528 begin
6529 if gTriggers <> nil then
6530 for a := 0 to High(gTriggers) do
6531 with gTriggers[a] do
6532 begin
6533 if TriggerType = TRIGGER_NONE then
6534 Continue;
6536 if TexturePanel = _id then
6537 TexturePanel := High(gPanels)
6538 else
6539 if TexturePanel > _id then
6540 Dec(TexturePanel);
6542 case TriggerType of
6543 TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR,
6544 TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP,
6545 TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT:
6546 if Data.PanelID = _id then
6547 Data.PanelID := High(gPanels)
6548 else
6549 if Data.PanelID > _id then
6550 Dec(Data.PanelID);
6552 TRIGGER_SHOT:
6553 if Data.ShotPanelID = _id then
6554 Data.ShotPanelID := High(gPanels)
6555 else
6556 if Data.ShotPanelID > _id then
6557 Dec(Data.ShotPanelID);
6558 end;
6559 end;
6561 tmp := gPanels[_id];
6563 for a := _id to High(gPanels)-1 do
6564 gPanels[a] := gPanels[a+1];
6566 gPanels[High(gPanels)] := tmp;
6568 ID := High(gPanels);
6569 end;
6570 end;
6572 procedure TMainForm.aMoveToBack(Sender: TObject);
6573 var
6574 a: Integer;
6575 begin
6576 if SelectedObjects = nil then
6577 Exit;
6579 for a := 0 to High(SelectedObjects) do
6580 with SelectedObjects[a] do
6581 if Live and (ObjectType = OBJECT_PANEL) then
6582 begin
6583 SelectedObjects[0] := SelectedObjects[a];
6584 SetLength(SelectedObjects, 1);
6585 MovePanel(ID, 0);
6586 FillProperty();
6587 Break;
6588 end;
6589 end;
6591 procedure TMainForm.aMoveToFore(Sender: TObject);
6592 var
6593 a: Integer;
6594 begin
6595 if SelectedObjects = nil then
6596 Exit;
6598 for a := 0 to High(SelectedObjects) do
6599 with SelectedObjects[a] do
6600 if Live and (ObjectType = OBJECT_PANEL) then
6601 begin
6602 SelectedObjects[0] := SelectedObjects[a];
6603 SetLength(SelectedObjects, 1);
6604 MovePanel(ID, 1);
6605 FillProperty();
6606 Break;
6607 end;
6608 end;
6610 procedure TMainForm.aSaveMapAsExecute(Sender: TObject);
6611 var
6612 idx: Integer;
6613 begin
6614 SaveDialog.Filter := MsgFileFilterWad;
6616 if not SaveDialog.Execute() then
6617 Exit;
6619 SaveMapForm.GetMaps(SaveDialog.FileName, True);
6621 if SaveMapForm.ShowModal() <> mrOK then
6622 Exit;
6624 SaveDialog.InitialDir := ExtractFileDir(SaveDialog.FileName);
6625 OpenedMap := SaveDialog.FileName+':\'+SaveMapForm.eMapName.Text;
6626 OpenedWAD := SaveDialog.FileName;
6628 idx := RecentFiles.IndexOf(OpenedMap);
6629 // Такая карта уже недавно открывалась:
6630 if idx >= 0 then
6631 RecentFiles.Delete(idx);
6632 RecentFiles.Insert(0, OpenedMap);
6633 RefreshRecentMenu;
6635 SaveMap(OpenedMap);
6637 gMapInfo.FileName := SaveDialog.FileName;
6638 gMapInfo.MapName := SaveMapForm.eMapName.Text;
6639 UpdateCaption(gMapInfo.Name, ExtractFileName(gMapInfo.FileName), gMapInfo.MapName);
6640 end;
6642 procedure TMainForm.aSelectAllExecute(Sender: TObject);
6643 var
6644 a: Integer;
6645 begin
6646 RemoveSelectFromObjects();
6648 case pcObjects.ActivePageIndex+1 of
6649 OBJECT_PANEL:
6650 if gPanels <> nil then
6651 for a := 0 to High(gPanels) do
6652 if gPanels[a].PanelType <> PANEL_NONE then
6653 SelectObject(OBJECT_PANEL, a, True);
6654 OBJECT_ITEM:
6655 if gItems <> nil then
6656 for a := 0 to High(gItems) do
6657 if gItems[a].ItemType <> ITEM_NONE then
6658 SelectObject(OBJECT_ITEM, a, True);
6659 OBJECT_MONSTER:
6660 if gMonsters <> nil then
6661 for a := 0 to High(gMonsters) do
6662 if gMonsters[a].MonsterType <> MONSTER_NONE then
6663 SelectObject(OBJECT_MONSTER, a, True);
6664 OBJECT_AREA:
6665 if gAreas <> nil then
6666 for a := 0 to High(gAreas) do
6667 if gAreas[a].AreaType <> AREA_NONE then
6668 SelectObject(OBJECT_AREA, a, True);
6669 OBJECT_TRIGGER:
6670 if gTriggers <> nil then
6671 for a := 0 to High(gTriggers) do
6672 if gTriggers[a].TriggerType <> TRIGGER_NONE then
6673 SelectObject(OBJECT_TRIGGER, a, True);
6674 end;
6676 RecountSelectedObjects();
6677 end;
6679 procedure TMainForm.tbGridOnClick(Sender: TObject);
6680 begin
6681 DotEnable := not DotEnable;
6682 (Sender as TToolButton).Down := DotEnable;
6683 end;
6685 procedure TMainForm.OnIdle(Sender: TObject; var Done: Boolean);
6686 var f: AnsiString;
6687 begin
6688 // FIXME: this is a shitty hack
6689 if not gDataLoaded then
6690 begin
6691 e_WriteLog('Init OpenGL', MSG_NOTIFY);
6692 e_InitGL();
6693 e_WriteLog('Loading data', MSG_NOTIFY);
6694 LoadStdFont('STDTXT', 'STDFONT', gEditorFont);
6695 e_WriteLog('Loading more data', MSG_NOTIFY);
6696 LoadData();
6697 e_WriteLog('Loading even more data', MSG_NOTIFY);
6698 gDataLoaded := True;
6699 MainForm.FormResize(nil);
6700 end;
6701 Draw();
6702 if StartMap <> '' then
6703 begin
6704 f := StartMap;
6705 StartMap := '';
6706 OpenMap(f, '');
6707 end;
6708 end;
6710 procedure TMainForm.miMapPreviewClick(Sender: TObject);
6711 begin
6712 if PreviewMode = 2 then
6713 Exit;
6715 if PreviewMode = 0 then
6716 begin
6717 Splitter2.Visible := False;
6718 Splitter1.Visible := False;
6719 StatusBar.Visible := False;
6720 PanelObjs.Visible := False;
6721 PanelProps.Visible := False;
6722 MainToolBar.Visible := False;
6723 sbHorizontal.Visible := False;
6724 sbVertical.Visible := False;
6725 end
6726 else
6727 begin
6728 StatusBar.Visible := True;
6729 PanelObjs.Visible := True;
6730 PanelProps.Visible := True;
6731 Splitter2.Visible := True;
6732 Splitter1.Visible := True;
6733 MainToolBar.Visible := True;
6734 sbHorizontal.Visible := True;
6735 sbVertical.Visible := True;
6736 end;
6738 PreviewMode := PreviewMode xor 1;
6739 (Sender as TMenuItem).Checked := PreviewMode > 0;
6741 FormResize(Self);
6742 end;
6744 procedure TMainForm.miLayer1Click(Sender: TObject);
6745 begin
6746 SwitchLayer(LAYER_BACK);
6747 end;
6749 procedure TMainForm.miLayer2Click(Sender: TObject);
6750 begin
6751 SwitchLayer(LAYER_WALLS);
6752 end;
6754 procedure TMainForm.miLayer3Click(Sender: TObject);
6755 begin
6756 SwitchLayer(LAYER_FOREGROUND);
6757 end;
6759 procedure TMainForm.miLayer4Click(Sender: TObject);
6760 begin
6761 SwitchLayer(LAYER_STEPS);
6762 end;
6764 procedure TMainForm.miLayer5Click(Sender: TObject);
6765 begin
6766 SwitchLayer(LAYER_WATER);
6767 end;
6769 procedure TMainForm.miLayer6Click(Sender: TObject);
6770 begin
6771 SwitchLayer(LAYER_ITEMS);
6772 end;
6774 procedure TMainForm.miLayer7Click(Sender: TObject);
6775 begin
6776 SwitchLayer(LAYER_MONSTERS);
6777 end;
6779 procedure TMainForm.miLayer8Click(Sender: TObject);
6780 begin
6781 SwitchLayer(LAYER_AREAS);
6782 end;
6784 procedure TMainForm.miLayer9Click(Sender: TObject);
6785 begin
6786 SwitchLayer(LAYER_TRIGGERS);
6787 end;
6789 procedure TMainForm.tbShowClick(Sender: TObject);
6790 var
6791 a: Integer;
6792 b: Boolean;
6793 begin
6794 b := True;
6795 for a := 0 to High(LayerEnabled) do
6796 b := b and LayerEnabled[a];
6798 b := not b;
6800 ShowLayer(LAYER_BACK, b);
6801 ShowLayer(LAYER_WALLS, b);
6802 ShowLayer(LAYER_FOREGROUND, b);
6803 ShowLayer(LAYER_STEPS, b);
6804 ShowLayer(LAYER_WATER, b);
6805 ShowLayer(LAYER_ITEMS, b);
6806 ShowLayer(LAYER_MONSTERS, b);
6807 ShowLayer(LAYER_AREAS, b);
6808 ShowLayer(LAYER_TRIGGERS, b);
6809 end;
6811 procedure TMainForm.miMiniMapClick(Sender: TObject);
6812 begin
6813 SwitchMap();
6814 end;
6816 procedure TMainForm.miSwitchGridClick(Sender: TObject);
6817 begin
6818 if DotStep = DotStepOne then
6819 DotStep := DotStepTwo
6820 else
6821 DotStep := DotStepOne;
6823 MousePos.X := (MousePos.X div DotStep) * DotStep;
6824 MousePos.Y := (MousePos.Y div DotStep) * DotStep;
6825 end;
6827 procedure TMainForm.miShowEdgesClick(Sender: TObject);
6828 begin
6829 ShowEdges();
6830 end;
6832 procedure TMainForm.miSnapToGridClick(Sender: TObject);
6833 begin
6834 SnapToGrid := not SnapToGrid;
6836 MousePos.X := (MousePos.X div DotStep) * DotStep;
6837 MousePos.Y := (MousePos.Y div DotStep) * DotStep;
6839 miSnapToGrid.Checked := SnapToGrid;
6840 end;
6842 procedure TMainForm.minexttabClick(Sender: TObject);
6843 begin
6844 if pcObjects.ActivePageIndex < pcObjects.PageCount-1 then
6845 pcObjects.ActivePageIndex := pcObjects.ActivePageIndex+1
6846 else
6847 pcObjects.ActivePageIndex := 0;
6848 end;
6850 procedure TMainForm.miSaveMiniMapClick(Sender: TObject);
6851 begin
6852 SaveMiniMapForm.ShowModal();
6853 end;
6855 procedure TMainForm.bClearTextureClick(Sender: TObject);
6856 begin
6857 lbTextureList.ItemIndex := -1;
6858 lTextureWidth.Caption := '';
6859 lTextureHeight.Caption := '';
6860 end;
6862 procedure TMainForm.miPackMapClick(Sender: TObject);
6863 begin
6864 PackMapForm.ShowModal();
6865 end;
6867 type SSArray = array of String;
6869 function ParseString (Str: AnsiString): SSArray;
6870 function GetStr (var Str: AnsiString): AnsiString;
6871 var a, b: Integer;
6872 begin
6873 Result := '';
6874 if Str[1] = '"' then
6875 for b := 1 to Length(Str) do
6876 if (b = Length(Str)) or (Str[b + 1] = '"') then
6877 begin
6878 Result := Copy(Str, 2, b - 1);
6879 Delete(Str, 1, b + 1);
6880 Str := Trim(Str);
6881 Exit;
6882 end;
6883 for a := 1 to Length(Str) do
6884 if (a = Length(Str)) or (Str[a + 1] = ' ') then
6885 begin
6886 Result := Copy(Str, 1, a);
6887 Delete(Str, 1, a + 1);
6888 Str := Trim(Str);
6889 Exit;
6890 end;
6891 end;
6892 begin
6893 Result := nil;
6894 Str := Trim(Str);
6895 while Str <> '' do
6896 begin
6897 SetLength(Result, Length(Result)+1);
6898 Result[High(Result)] := GetStr(Str);
6899 end;
6900 end;
6902 procedure TMainForm.miTestMapClick(Sender: TObject);
6903 var
6904 newWAD, oldWAD, tempMap, ext: String;
6905 args: SSArray;
6906 opt: LongWord;
6907 time, i: Integer;
6908 proc: TProcessUTF8;
6909 res: Boolean;
6910 begin
6911 // Ignore while map testing in progress
6912 if MapTestProcess <> nil then
6913 Exit;
6915 // Сохраняем временную карту:
6916 time := 0;
6917 repeat
6918 newWAD := Format('%s/temp%.4d', [MapsDir, time]);
6919 Inc(time);
6920 until not FileExists(newWAD);
6921 if OpenedMap <> '' then
6922 begin
6923 oldWad := g_ExtractWadName(OpenedMap);
6924 newWad := newWad + ExtractFileExt(oldWad);
6925 if CopyFile(oldWad, newWad) = false then
6926 e_WriteLog('MapTest: unable to copy [' + oldWad + '] to [' + newWad + ']', MSG_WARNING)
6927 end
6928 else
6929 begin
6930 newWad := newWad + '.wad'
6931 end;
6932 tempMap := newWAD + ':\' + TEST_MAP_NAME;
6933 SaveMap(tempMap);
6935 // Опции игры:
6936 opt := 32 + 64;
6937 if TestOptionsTwoPlayers then
6938 opt := opt + 1;
6939 if TestOptionsTeamDamage then
6940 opt := opt + 2;
6941 if TestOptionsAllowExit then
6942 opt := opt + 4;
6943 if TestOptionsWeaponStay then
6944 opt := opt + 8;
6945 if TestOptionsMonstersDM then
6946 opt := opt + 16;
6948 // Запускаем:
6949 proc := TProcessUTF8.Create(nil);
6950 proc.Executable := TestD2dExe;
6951 {$IFDEF DARWIN}
6952 // TODO: get real executable name from Info.plist
6953 if LowerCase(ExtractFileExt(TestD2dExe)) = '.app' then
6954 proc.Executable := TestD2dExe + DirectorySeparator + 'Contents' + DirectorySeparator + 'MacOS' + DirectorySeparator + 'Doom2DF';
6955 {$ENDIF}
6956 proc.Parameters.Add('-map');
6957 proc.Parameters.Add(tempMap);
6958 proc.Parameters.Add('-gm');
6959 proc.Parameters.Add(TestGameMode);
6960 proc.Parameters.Add('-limt');
6961 proc.Parameters.Add(TestLimTime);
6962 proc.Parameters.Add('-lims');
6963 proc.Parameters.Add(TestLimScore);
6964 proc.Parameters.Add('-opt');
6965 proc.Parameters.Add(IntToStr(opt));
6966 proc.Parameters.Add('--debug');
6967 if TestMapOnce then
6968 proc.Parameters.Add('--close');
6970 args := ParseString(TestD2DArgs);
6971 for i := 0 to High(args) do
6972 proc.Parameters.Add(args[i]);
6974 res := True;
6975 try
6976 proc.Execute();
6977 except
6978 res := False;
6979 end;
6980 if res then
6981 begin
6982 tbTestMap.Enabled := False;
6983 MapTestFile := newWAD;
6984 MapTestProcess := proc;
6985 end
6986 else
6987 begin
6988 Application.MessageBox(PChar(MsgMsgExecError), 'FIXME', MB_OK or MB_ICONERROR);
6989 SysUtils.DeleteFile(newWAD);
6990 proc.Free();
6991 end;
6992 end;
6994 procedure TMainForm.sbVerticalScroll(Sender: TObject;
6995 ScrollCode: TScrollCode; var ScrollPos: Integer);
6996 begin
6997 MapOffset.Y := -sbVertical.Position;
6998 RenderPanel.Invalidate;
6999 end;
7001 procedure TMainForm.sbHorizontalScroll(Sender: TObject;
7002 ScrollCode: TScrollCode; var ScrollPos: Integer);
7003 begin
7004 MapOffset.X := -sbHorizontal.Position;
7005 RenderPanel.Invalidate;
7006 end;
7008 procedure TMainForm.miOpenWadMapClick(Sender: TObject);
7009 begin
7010 if OpenedWAD <> '' then
7011 begin
7012 OpenMap(OpenedWAD, '');
7013 end;
7014 end;
7016 procedure TMainForm.selectall1Click(Sender: TObject);
7017 var
7018 a: Integer;
7019 begin
7020 RemoveSelectFromObjects();
7022 if gPanels <> nil then
7023 for a := 0 to High(gPanels) do
7024 if gPanels[a].PanelType <> PANEL_NONE then
7025 SelectObject(OBJECT_PANEL, a, True);
7027 if gItems <> nil then
7028 for a := 0 to High(gItems) do
7029 if gItems[a].ItemType <> ITEM_NONE then
7030 SelectObject(OBJECT_ITEM, a, True);
7032 if gMonsters <> nil then
7033 for a := 0 to High(gMonsters) do
7034 if gMonsters[a].MonsterType <> MONSTER_NONE then
7035 SelectObject(OBJECT_MONSTER, a, True);
7037 if gAreas <> nil then
7038 for a := 0 to High(gAreas) do
7039 if gAreas[a].AreaType <> AREA_NONE then
7040 SelectObject(OBJECT_AREA, a, True);
7042 if gTriggers <> nil then
7043 for a := 0 to High(gTriggers) do
7044 if gTriggers[a].TriggerType <> TRIGGER_NONE then
7045 SelectObject(OBJECT_TRIGGER, a, True);
7047 RecountSelectedObjects();
7048 end;
7050 procedure TMainForm.Splitter1CanResize(Sender: TObject;
7051 var NewSize: Integer; var Accept: Boolean);
7052 begin
7053 Accept := (NewSize > 140);
7054 end;
7056 procedure TMainForm.Splitter2CanResize(Sender: TObject;
7057 var NewSize: Integer; var Accept: Boolean);
7058 begin
7059 Accept := (NewSize > 110);
7060 end;
7062 procedure TMainForm.vleObjectPropertyEnter(Sender: TObject);
7063 begin
7064 EditingProperties := True;
7065 end;
7067 procedure TMainForm.vleObjectPropertyExit(Sender: TObject);
7068 begin
7069 EditingProperties := False;
7070 end;
7072 procedure TMainForm.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
7073 begin
7074 // Объекты передвигались:
7075 if MainForm.ActiveControl = RenderPanel then
7076 begin
7077 if (Key = VK_NUMPAD4) or
7078 (Key = VK_NUMPAD6) or
7079 (Key = VK_NUMPAD8) or
7080 (Key = VK_NUMPAD5) or
7081 (Key = Ord('V')) then
7082 FillProperty();
7083 end;
7084 // Быстрое превью карты:
7085 if Key = Ord('E') then
7086 begin
7087 if PreviewMode = 2 then
7088 PreviewMode := 0;
7089 end;
7090 RenderPanelMouseMove(Sender, Shift, RenderMousePos().X, RenderMousePos().Y);
7091 end;
7093 end.