Statistics
| Branch: | Tag: | Revision:

root / Client / UfrmMain.pas @ 119:66352054ce4d

History | View | Annotate | Download (89.4 kB)

1
(*
2
 * CDDL HEADER START
3
 *
4
 * The contents of this file are subject to the terms of the
5
 * Common Development and Distribution License, Version 1.0 only
6
 * (the "License").  You may not use this file except in compliance
7
 * with the License.
8
 *
9
 * You can obtain a copy of the license at
10
 * http://www.opensource.org/licenses/cddl1.php.
11
 * See the License for the specific language governing permissions
12
 * and limitations under the License.
13
 *
14
 * When distributing Covered Code, include this CDDL HEADER in each
15
 * file and include the License file at
16
 * http://www.opensource.org/licenses/cddl1.php.  If applicable,
17
 * add the following below this CDDL HEADER, with the fields enclosed
18
 * by brackets "[]" replaced with your own identifying * information:
19
 *      Portions Copyright [yyyy] [name of copyright owner]
20
 *
21
 * CDDL HEADER END
22
 *
23
 *
24
 *      Portions Copyright 2009 Andreas Schneider
25
 *)
26
unit UfrmMain;
27
28
{$mode objfpc}{$H+}
29
30
interface
31
32
uses
33
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Menus,
34
  ComCtrls, OpenGLContext, GL, GLu, UGameResources, ULandscape, ExtCtrls,
35
  StdCtrls, Spin, UEnums, VirtualTrees, Buttons, UMulBlock, UWorldItem, math,
36
  LCLIntf, UOverlayUI, UStatics, UEnhancedMemoryStream, ActnList,
37
  XMLPropStorage, fgl, ImagingClasses, dateutils, UPlatformTypes, UMap, UPacket,
38
  UGLFont, DOM, XMLRead, XMLWrite;
39
40
type
41
  TAccessChangedListener = procedure(AAccessLevel: TAccessLevel) of object;
42
  TScreenBufferState = (sbsValid, sbsIndexed, sbsFiltered);
43
  TScreenBufferStates = set of TScreenBufferState;
44
45
  TGhostTile = class(TStaticItem);
46
  TPacketList = specialize TFPGObjectList<TPacket>;
47
48
  { TfrmMain }
49
50
  TfrmMain = class(TForm)
51
    acSelect: TAction;
52
    acDraw: TAction;
53
    acMove: TAction;
54
    acElevate: TAction;
55
    acDelete: TAction;
56
    acHue: TAction;
57
    acBoundaries: TAction;
58
    acFilter: TAction;
59
    acFlat: TAction;
60
    acNoDraw: TAction;
61
    acUndo: TAction;
62
    acVirtualLayer: TAction;
63
    ActionList1: TActionList;
64
    ApplicationProperties1: TApplicationProperties;
65
    btnAddLocation: TSpeedButton;
66
    btnAddRandom: TSpeedButton;
67
    btnClearLocations: TSpeedButton;
68
    btnClearRandom: TSpeedButton;
69
    btnDeleteLocation: TSpeedButton;
70
    btnDeleteRandom: TSpeedButton;
71
    btnGoTo: TButton;
72
    btnRandomPresetDelete: TSpeedButton;
73
    btnRandomPresetSave: TSpeedButton;
74
    cbRandomPreset: TComboBox;
75
    cbStatics: TCheckBox;
76
    cbTerrain: TCheckBox;
77
    edChat: TEdit;
78
    edFilter: TEdit;
79
    edSearchID: TEdit;
80
    gbRandom: TGroupBox;
81
    ImageList1: TImageList;
82
    lblChatHeaderCaption: TLabel;
83
    lblFilter: TLabel;
84
    lblTipC: TLabel;
85
    lblTip: TLabel;
86
    lblTileInfo: TLabel;
87
    lblX: TLabel;
88
    lblY: TLabel;
89
    lbClients: TListBox;
90
    MainMenu1: TMainMenu;
91
    mnuSecurityQuestion: TMenuItem;
92
    mnuShowAnimations: TMenuItem;
93
    mnuSettings: TMenuItem;
94
    mnuFlatShowHeight: TMenuItem;
95
    mnuGrabHue: TMenuItem;
96
    mnuGrabTileID: TMenuItem;
97
    mnuRegionControl: TMenuItem;
98
    mnuVirtualLayer: TMenuItem;
99
    mnuLargeScaleCommands: TMenuItem;
100
    mnuSetHue: TMenuItem;
101
    mnuGoToClient: TMenuItem;
102
    mnuAbout: TMenuItem;
103
    mnuHelp: TMenuItem;
104
    mnuSeparator3: TMenuItem;
105
    mnuBoundaries: TMenuItem;
106
    mnuSelect: TMenuItem;
107
    mnuDraw: TMenuItem;
108
    mnuMove: TMenuItem;
109
    mnuElevate: TMenuItem;
110
    mnuDelete: TMenuItem;
111
    mnuAddToRandom: TMenuItem;
112
    mnuFlush: TMenuItem;
113
    mnuShutdown: TMenuItem;
114
    mnuSeparator2: TMenuItem;
115
    mnuAccountControl: TMenuItem;
116
    mnuAdministration: TMenuItem;
117
    mnuSeparator1: TMenuItem;
118
    mnuExit: TMenuItem;
119
    mnuDisconnect: TMenuItem;
120
    mnuCentrED: TMenuItem;
121
    oglGameWindow: TOpenGLControl;
122
    pcLeft: TPageControl;
123
    pmGrabTileInfo: TPopupMenu;
124
    pnlBottom: TPanel;
125
    edX: TSpinEdit;
126
    edY: TSpinEdit;
127
    pmTileList: TPopupMenu;
128
    pmTools: TPopupMenu;
129
    pmClients: TPopupMenu;
130
    pnlChat: TPanel;
131
    pnlChatHeader: TPanel;
132
    pmFlatViewSettings: TPopupMenu;
133
    spChat: TSplitter;
134
    spTileList: TSplitter;
135
    tbFilter: TToolButton;
136
    tbFlat: TToolButton;
137
    tbNoDraw: TToolButton;
138
    tmTileHint: TTimer;
139
    tbSeparator2: TToolButton;
140
    tbUndo: TToolButton;
141
    tsLocations: TTabSheet;
142
    tbSetHue: TToolButton;
143
    tmGrabTileInfo: TTimer;
144
    tmMovement: TTimer;
145
    tbSeparator5: TToolButton;
146
    tbRadarMap: TToolButton;
147
    tbVirtualLayer: TToolButton;
148
    tsClients: TTabSheet;
149
    tbMain: TToolBar;
150
    tbDisconnect: TToolButton;
151
    tbSeparator1: TToolButton;
152
    tbSelect: TToolButton;
153
    tbDrawTile: TToolButton;
154
    tbMoveTile: TToolButton;
155
    tbElevateTile: TToolButton;
156
    tbDeleteTile: TToolButton;
157
    tbSeparator3: TToolButton;
158
    tbBoundaries: TToolButton;
159
    tbSeparator4: TToolButton;
160
    tbTerrain: TToolButton;
161
    tbStatics: TToolButton;
162
    tsTiles: TTabSheet;
163
    vdtTiles: TVirtualDrawTree;
164
    vdtRandom: TVirtualDrawTree;
165
    vstChat: TVirtualStringTree;
166
    vstLocations: TVirtualStringTree;
167
    XMLPropStorage1: TXMLPropStorage;
168
    procedure acBoundariesExecute(Sender: TObject);
169
    procedure acDeleteExecute(Sender: TObject);
170
    procedure acDrawExecute(Sender: TObject);
171
    procedure acElevateExecute(Sender: TObject);
172
    procedure acFilterExecute(Sender: TObject);
173
    procedure acFlatExecute(Sender: TObject);
174
    procedure acHueExecute(Sender: TObject);
175
    procedure acMoveExecute(Sender: TObject);
176
    procedure acNoDrawExecute(Sender: TObject);
177
    procedure acSelectExecute(Sender: TObject);
178
    procedure acUndoExecute(Sender: TObject);
179
    procedure acVirtualLayerExecute(Sender: TObject);
180
    procedure ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);
181
    procedure btnAddLocationClick(Sender: TObject);
182
    procedure btnAddRandomClick(Sender: TObject);
183
    procedure btnClearLocationsClick(Sender: TObject);
184
    procedure btnClearRandomClick(Sender: TObject);
185
    procedure btnDeleteLocationClick(Sender: TObject);
186
    procedure btnDeleteRandomClick(Sender: TObject);
187
    procedure btnGoToClick(Sender: TObject);
188
    procedure btnRandomPresetDeleteClick(Sender: TObject);
189
    procedure btnRandomPresetSaveClick(Sender: TObject);
190
    procedure cbRandomPresetChange(Sender: TObject);
191
    procedure cbStaticsChange(Sender: TObject);
192
    procedure cbTerrainChange(Sender: TObject);
193
    procedure edChatKeyPress(Sender: TObject; var Key: char);
194
    procedure edFilterEditingDone(Sender: TObject);
195
    procedure FormActivate(Sender: TObject);
196
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
197
    procedure FormCreate(Sender: TObject);
198
    procedure FormDestroy(Sender: TObject);
199
    procedure edSearchIDExit(Sender: TObject);
200
    procedure edSearchIDKeyPress(Sender: TObject; var Key: char);
201
    procedure lblChatHeaderCaptionClick(Sender: TObject);
202
    procedure lblChatHeaderCaptionMouseEnter(Sender: TObject);
203
    procedure lblChatHeaderCaptionMouseLeave(Sender: TObject);
204
    procedure mnuAboutClick(Sender: TObject);
205
    procedure mnuAccountControlClick(Sender: TObject);
206
    procedure mnuDisconnectClick(Sender: TObject);
207
    procedure mnuExitClick(Sender: TObject);
208
    procedure mnuFlatShowHeightClick(Sender: TObject);
209
    procedure mnuFlushClick(Sender: TObject);
210
    procedure mnuGoToClientClick(Sender: TObject);
211
    procedure mnuGrabHueClick(Sender: TObject);
212
    procedure mnuGrabTileIDClick(Sender: TObject);
213
    procedure mnuLargeScaleCommandsClick(Sender: TObject);
214
    procedure mnuRegionControlClick(Sender: TObject);
215
    procedure mnuShowAnimationsClick(Sender: TObject);
216
    procedure mnuShutdownClick(Sender: TObject);
217
    procedure oglGameWindowDblClick(Sender: TObject);
218
    procedure oglGameWindowKeyDown(Sender: TObject; var Key: Word;
219
      Shift: TShiftState);
220
    procedure oglGameWindowMouseDown(Sender: TObject; Button: TMouseButton;
221
      Shift: TShiftState; X, Y: Integer);
222
    procedure oglGameWindowMouseEnter(Sender: TObject);
223
    procedure oglGameWindowMouseLeave(Sender: TObject);
224
    procedure oglGameWindowMouseMove(Sender: TObject; Shift: TShiftState; X,
225
      Y: Integer);
226
    procedure oglGameWindowMouseUp(Sender: TObject; Button: TMouseButton;
227
      Shift: TShiftState; X, Y: Integer);
228
    procedure oglGameWindowMouseWheel(Sender: TObject; Shift: TShiftState;
229
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
230
    procedure oglGameWindowPaint(Sender: TObject);
231
    procedure oglGameWindowResize(Sender: TObject);
232
    procedure pmGrabTileInfoPopup(Sender: TObject);
233
    procedure tbFilterMouseMove(Sender: TObject; Shift: TShiftState; X,
234
      Y: Integer);
235
    procedure tbRadarMapClick(Sender: TObject);
236
    procedure tbStaticsClick(Sender: TObject);
237
    procedure tbTerrainClick(Sender: TObject);
238
    procedure tmGrabTileInfoTimer(Sender: TObject);
239
    procedure tmMovementTimer(Sender: TObject);
240
    procedure tmTileHintTimer(Sender: TObject);
241
    procedure vdtRandomClick(Sender: TObject);
242
    procedure vdtRandomDragDrop(Sender: TBaseVirtualTree; Source: TObject;
243
      DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState;
244
      Pt: TPoint; var Effect: Integer; Mode: TDropMode);
245
    procedure vdtRandomDragOver(Sender: TBaseVirtualTree; Source: TObject;
246
      Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode;
247
      var Effect: Integer; var Accept: Boolean);
248
    procedure vdtRandomLoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
249
      Stream: TStream);
250
    procedure vdtRandomSaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode;
251
      Stream: TStream);
252
    procedure vdtRandomUpdating(Sender: TBaseVirtualTree; State: TVTUpdateState);
253
    procedure vdtTilesClick(Sender: TObject);
254
    procedure vdtTilesDrawNode(Sender: TBaseVirtualTree;
255
      const PaintInfo: TVTPaintInfo);
256
    procedure vdtTilesEnter(Sender: TObject);
257
    procedure vdtTilesExit(Sender: TObject);
258
    procedure vdtTilesHotChange(Sender: TBaseVirtualTree; OldNode,
259
      NewNode: PVirtualNode);
260
    procedure vdtTilesKeyPress(Sender: TObject; var Key: char);
261
    procedure vdtTilesMouseMove(Sender: TObject; Shift: TShiftState; X,
262
      Y: Integer);
263
    procedure vdtTilesScroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
264
    procedure vstChatClick(Sender: TObject);
265
    procedure vstChatFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
266
    procedure vstChatGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
267
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
268
    procedure vstChatPaintText(Sender: TBaseVirtualTree;
269
      const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
270
      TextType: TVSTTextType);
271
    procedure vstLocationsDblClick(Sender: TObject);
272
    procedure vstLocationsFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode
273
      );
274
    procedure vstLocationsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
275
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
276
    procedure vstLocationsLoadNode(Sender: TBaseVirtualTree;
277
      Node: PVirtualNode; Stream: TStream);
278
    procedure vstLocationsNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
279
      Column: TColumnIndex; const NewText: String);
280
    procedure vstLocationsSaveNode(Sender: TBaseVirtualTree;
281
      Node: PVirtualNode; Stream: TStream);
282
    procedure XMLPropStorage1RestoreProperties(Sender: TObject);
283
  protected
284
    { Members }
285
    FAppDir: String;
286
    FConfigDir: String;
287
    FX: Integer;
288
    FY: Integer;
289
    FDrawDistance: Integer;
290
    FLowOffsetX: Integer;
291
    FLowOffsetY: Integer;
292
    FHighOffsetX: Integer;
293
    FHighOffsetY: Integer;
294
    FRangeX: Integer;
295
    FRangeY: Integer;
296
    FLandscape: TLandscape;
297
    FTextureManager: TLandTextureManager;
298
    FScreenBuffer: TScreenBuffer;
299
    FScreenBufferState: TScreenBufferStates;
300
    FCurrentTile: TWorldItem;
301
    FSelectedTile: TWorldItem;
302
    FVirtualTiles: TWorldItemList;
303
    FVLayerImage: TSingleImage;
304
    FVLayerMaterial: TMaterial;
305
    FOverlayUI: TOverlayUI;
306
    FLocationsFile: string;
307
    FRandomPresetsFile: string;
308
    FRandomPresetsDoc: TXMLDocument;
309
    FLastDraw: TDateTime;
310
    FAccessChangedListeners: array of TAccessChangedListener;
311
    FRepaintNeeded: Boolean;
312
    FSelection: TRect;
313
    FUndoList: TPacketList;
314
    FGLFont: TGLFont;
315
    { Methods }
316
    procedure BuildTileList;
317
    function  ConfirmAction: Boolean;
318
    function  FindRandomPreset(AName: String): TDOMElement;
319
    procedure ForceUpdateCurrentTile;
320
    procedure GetDrawOffset(ARelativeX, ARelativeY: Integer; out DrawX,
321
      DrawY: Integer); inline;
322
    function  GetInternalTileID(ATile: TWorldItem): Word;
323
    function  GetSelectedRect: TRect;
324
    procedure InitRender;
325
    procedure InitSize;
326
    procedure LoadLocations;
327
    procedure LoadRandomPresets;
328
    procedure MoveBy(AOffsetX, AOffsetY: Integer); inline;
329
    procedure PrepareMapCell(AMapCell: TMapCell);
330
    procedure PrepareScreenBlock(ABlockInfo: PBlockInfo);
331
    procedure ProcessToolState;
332
    procedure ProcessAccessLevel;
333
    procedure RebuildScreenBuffer;
334
    procedure Render;
335
    procedure SaveLocations;
336
    procedure SaveRandomPresets;
337
    procedure SetCurrentTile(const AValue: TWorldItem);
338
    procedure SetDarkLights; inline;
339
    procedure SetNormalLights; inline;
340
    procedure SetSelectedTile(const AValue: TWorldItem);
341
    procedure SetX(const AValue: Integer);
342
    procedure SetY(const AValue: Integer);
343
    procedure UpdateCurrentTile;
344
    procedure UpdateCurrentTile(AX, AY: Integer);
345
    procedure UpdateFilter;
346
    procedure UpdateSelection;
347
    procedure WriteChatMessage(ASender, AMessage: string);
348
    { Events }
349
    procedure OnClientHandlingPacket(ABuffer: TEnhancedMemoryStream);
350
    procedure OnLandscapeChanged;
351
    procedure OnMapChanged(AMapCell: TMapCell);
352
    procedure OnNewBlock(ABlock: TBlock);
353
    procedure OnStaticDeleted(AStaticItem: TStaticItem);
354
    procedure OnStaticElevated(AStaticItem: TStaticItem);
355
    procedure OnStaticHued(AStaticItem: TStaticItem);
356
    procedure OnStaticInserted(AStaticItem: TStaticItem);
357
    procedure OnTileRemoved(ATile: TMulBlock);
358
  public
359
    { Fields }
360
    property X: Integer read FX write SetX;
361
    property Y: Integer read FY write SetY;
362
    property Landscape: TLandscape read FLandscape;
363
    property CurrentTile: TWorldItem read FCurrentTile write SetCurrentTile;
364
    property SelectedTile: TWorldItem read FSelectedTile write SetSelectedTile;
365
    { Methods }
366
    procedure InvalidateFilter;
367
    procedure InvalidateScreenBuffer;
368
    procedure RegisterAccessChangedListener(AListener: TAccessChangedListener);
369
    procedure SetPos(AX, AY: Word);
370
    procedure UnregisterAccessChangedListener(AListener: TAccessChangedListener);
371
  end; 
372
373
var
374
  frmMain: TfrmMain;
375
376
implementation
377
378
uses
379
  UdmNetwork, UArt, UTiledata, UAdminHandling, UPackets,
380
  UfrmAccountControl, UGraphicHelper, ImagingComponents, UfrmDrawSettings,
381
  UfrmBoundaries, UfrmElevateSettings, UfrmConfirmation, UfrmMoveSettings,
382
  UfrmAbout, UPacketHandlers, UfrmHueSettings, UfrmRadar, UfrmLargeScaleCommand,
383
  UfrmLogin, UResourceManager, UfrmVirtualLayer, UfrmFilter, UfrmTileInfo,
384
  UfrmRegionControl, Logging, LConvEncoding, LCLType;
385
386
type
387
  TGLArrayf4 = array[0..3] of GLfloat;
388
  PTileInfo = ^TTileInfo;
389
  TTileInfo = record
390
    ID: Word;
391
  end;
392
  PChatInfo = ^TChatInfo;
393
  TChatInfo = record
394
    Time: TDateTime;
395
    Sender: string;
396
    Msg: string;
397
  end;
398
  PLocationInfo = ^TLocationInfo;
399
  TLocationInfo = record
400
    X: Word;
401
    Y: Word;
402
    Name: string;
403
  end;
404
405
const
406
  CScreenBufferValid = [sbsValid, sbsIndexed, sbsFiltered];
407
408
function IsInRect(const AX, AY: Integer; const ARect: TRect): Boolean; inline;
409
begin
410
  Result := (AX >= ARect.Left) and
411
            (AX <= ARect.Right) and
412
            (AY >= ARect.Top) and
413
            (AY <= ARect.Bottom);
414
end;
415
416
{ TfrmMain }
417
418
procedure TfrmMain.mnuExitClick(Sender: TObject);
419
begin
420
  Close;
421
end;
422
423
procedure TfrmMain.mnuFlatShowHeightClick(Sender: TObject);
424
begin
425
  RebuildScreenBuffer;
426
end;
427
428
procedure TfrmMain.mnuFlushClick(Sender: TObject);
429
begin
430
  dmNetwork.Send(TFlushServerPacket.Create);
431
end;
432
433
procedure TfrmMain.mnuGoToClientClick(Sender: TObject);
434
begin
435
  if lbClients.ItemIndex > -1 then
436
    dmNetwork.Send(TGotoClientPosPacket.Create(lbClients.Items.Strings[lbClients.ItemIndex]));
437
end;
438
439
procedure TfrmMain.mnuGrabHueClick(Sender: TObject);
440
begin
441
  if CurrentTile is TStaticItem then
442
  begin
443
    frmHueSettings.lbHue.ItemIndex := TStaticItem(CurrentTile).Hue;
444
    frmFilter.JumpToHue(TStaticItem(CurrentTile).Hue);
445
  end;
446
end;
447
448
procedure TfrmMain.mnuGrabTileIDClick(Sender: TObject);
449
var
450
  internalTileID: Integer;
451
  node: PVirtualNode;
452
  tileInfo: PTileInfo;
453
begin
454
  if CurrentTile <> nil then
455
  begin
456
    internalTileID := GetInternalTileID(CurrentTile);
457
    node := vdtTiles.GetFirst;
458
    while node <> nil do
459
    begin
460
      tileInfo := vdtTiles.GetNodeData(node);
461
      if tileInfo^.ID = internalTileID then
462
      begin
463
        vdtTiles.ClearSelection;
464
        vdtTiles.Selected[node] := True;
465
        vdtTiles.FocusedNode := node;
466
        Break;
467
      end;
468
      node := vdtTiles.GetNext(node);
469
    end;
470
  end;
471
end;
472
473
procedure TfrmMain.mnuLargeScaleCommandsClick(Sender: TObject);
474
begin
475
  frmLargeScaleCommand.Show;
476
end;
477
478
procedure TfrmMain.mnuRegionControlClick(Sender: TObject);
479
begin
480
  frmRegionControl.Show;
481
end;
482
483
procedure TfrmMain.mnuShowAnimationsClick(Sender: TObject);
484
begin
485
  FTextureManager.UseAnims := mnuShowAnimations.Checked;
486
  RebuildScreenBuffer;
487
end;
488
489
procedure TfrmMain.mnuShutdownClick(Sender: TObject);
490
begin
491
  dmNetwork.Send(TQuitServerPacket.Create(''));
492
end;
493
494
procedure TfrmMain.oglGameWindowDblClick(Sender: TObject);
495
begin
496
  if (acSelect.Checked) and (CurrentTile <> nil) then
497
    btnAddRandomClick(Sender);
498
end;
499
500
procedure TfrmMain.oglGameWindowKeyDown(Sender: TObject; var Key: Word;
501
  Shift: TShiftState);
502
begin
503
  case Key of
504
    VK_W, VK_NUMPAD8, VK_UP:
505
      MoveBy(-8, -8);
506
    VK_S, VK_NUMPAD2, VK_DOWN:
507
      MoveBy(8, 8);
508
    VK_A, VK_NUMPAD4, VK_LEFT:
509
      MoveBy(-8, 8);
510
    VK_D, VK_NUMPAD6, VK_RIGHT:
511
      MoveBy(8, -8);
512
    VK_Q, VK_NUMPAD7:
513
      MoveBy(-8, 0);
514
    VK_E, VK_NUMPAD9:
515
      MoveBy(0, -8);
516
    VK_Y, VK_NUMPAD1:
517
      MoveBy(0, 8);
518
    VK_C, VK_NUMPAD3:
519
      MoveBy(8, 0);
520
  end;
521
end;
522
523
procedure TfrmMain.oglGameWindowMouseDown(Sender: TObject;
524
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
525
begin
526
  Logger.EnterMethod([lcClient, lcDebug], 'MouseDown');
527
  try
528
    if Button = mbRight then
529
      pmTools.PopUp(Mouse.CursorPos.X, Mouse.CursorPos.Y);
530
531
    if Button <> mbLeft then
532
      Exit;
533
534
    UpdateCurrentTile(X, Y);
535
    if FOverlayUI.ActiveArrow > -1 then
536
      tmMovement.Enabled := True;
537
538
    SelectedTile := CurrentTile;
539
    if CurrentTile = nil then Exit;
540
541
    if acSelect.Checked then                      //***** Selection Mode *****//
542
      tmGrabTileInfo.Enabled := True;
543
544
    FRepaintNeeded := True;
545
  finally
546
    Logger.ExitMethod([lcClient, lcDebug], 'MouseDown');
547
  end;
548
end;
549
550
procedure TfrmMain.oglGameWindowMouseEnter(Sender: TObject);
551
begin
552
  if Active then
553
    oglGameWindow.SetFocus;
554
555
  FOverlayUI.Visible := True;
556
557
  if frmFilter.Visible then
558
  begin
559
    frmFilter.Locked := True;
560
    frmFilter.Hide;
561
    frmFilter.Locked := False;
562
  end;
563
564
  FRepaintNeeded := True;
565
end;
566
567
procedure TfrmMain.oglGameWindowMouseLeave(Sender: TObject);
568
begin
569
  if not (frmConfirmation.Visible or
570
          (frmMoveSettings.Visible and (fsModal in frmMoveSettings.FormState))
571
         ) then //during confirmation the mouse would leave ...
572
  begin
573
    CurrentTile := nil;
574
    FOverlayUI.Visible := False;
575
  end;
576
577
  FRepaintNeeded := True;
578
end;
579
580
procedure TfrmMain.oglGameWindowMouseMove(Sender: TObject; Shift: TShiftState;
581
  X, Y: Integer);
582
var
583
  lastTile: TWorldItem;
584
  offsetX, offsetY: Integer;
585
begin
586
  Logger.EnterMethod([lcClient, lcDebug], 'MouseMove');
587
  lastTile := CurrentTile;
588
  
589
  if ssMiddle in Shift then
590
  begin
591
    UpdateCurrentTile(X, Y);
592
    if (lastTile <> nil) and (CurrentTile <> nil) and (lastTile <> CurrentTile) then
593
    begin
594
      offsetX := lastTile.X - CurrentTile.X;
595
      offsetY := lastTile.Y - CurrentTile.Y;
596
      if InRange(offsetX, -8, 8) and InRange(offsetY, -8, 8) then
597
        SetPos(FX - offsetX, FY - offsetY);
598
    end;
599
  end;
600
601
  UpdateCurrentTile(X, Y);
602
603
  FRepaintNeeded := True;
604
  Logger.ExitMethod([lcClient, lcDebug], 'MouseMove');
605
end;
606
607
procedure TfrmMain.oglGameWindowMouseUp(Sender: TObject; Button: TMouseButton;
608
  Shift: TShiftState; X, Y: Integer);
609
var
610
  map: TMapCell;
611
  i: Integer;
612
  z: ShortInt;
613
  blockInfo: PBlockInfo;
614
  targetRect: TRect;
615
  offsetX, offsetY: Integer;
616
  tile: TWorldItem;
617
  tileX, tileY, newX, newY: Word;
618
  targetTiles: TWorldItemList;
619
  targetTile: TWorldItem;
620
begin
621
  Logger.EnterMethod([lcClient, lcDebug], 'MouseUp');
622
  if Button <> mbLeft then
623
  begin
624
    Logger.ExitMethod([lcClient, lcDebug], 'MouseUp');
625
    Exit;
626
  end;
627
628
  UpdateCurrentTile(X, Y);
629
  tmMovement.Enabled := False;
630
  if CurrentTile = nil then
631
  begin
632
    SelectedTile := nil;
633
    Logger.ExitMethod([lcClient, lcDebug], 'MouseUp');
634
    Exit;
635
  end;
636
637
  targetTile := CurrentTile;
638
  
639
  if acSelect.Checked and tmGrabTileInfo.Enabled then
640
  begin
641
    tmGrabTileInfo.Enabled := False;
642
    mnuGrabTileIDClick(nil);
643
  end;
644
645
  if (not acSelect.Checked) and (targetTile <> nil) and (SelectedTile <> nil) then
646
  begin
647
    targetRect := GetSelectedRect;
648
    FUndoList.Clear;
649
650
    if (SelectedTile = targetTile) or ConfirmAction then
651
    begin
652
      if acDraw.Checked then                        //***** Drawing Mode *****//
653
      begin
654
        for tileX := FSelection.Left to FSelection.Right do
655
          for tileY := FSelection.Top to FSelection.Bottom do
656
          begin
657
            map := FLandscape.MapCell[tileX, tileY];
658
            if map.IsGhost then
659
            begin
660
              FUndoList.Add(TDrawMapPacket.Create(tileX, tileY, map.RawZ,
661
                map.RawTileID));
662
              dmNetwork.Send(TDrawMapPacket.Create(tileX, tileY, map.Z,
663
                map.TileID));
664
            end;
665
          end;
666
667
        Logger.Send([lcClient, lcDebug], 'Virtual tiles', FVirtualTiles.Count);
668
        for i := 0 to FVirtualTiles.Count - 1 do
669
        begin
670
          tile := FVirtualTiles[i];
671
          if tile is TGhostTile then
672
          begin
673
            dmNetwork.Send(TInsertStaticPacket.Create(tile.X, tile.Y, tile.Z,
674
              tile.TileID, TGhostTile(tile).Hue));
675
            FUndoList.Add(TDeleteStaticPacket.Create(TGhostTile(tile)));
676
          end;
677
        end;
678
      end else if (SelectedTile <> targetTile) or targetTile.CanBeEdited then
679
      begin
680
        if (not acMove.Checked) or (SelectedTile <> targetTile) or
681
           (not frmMoveSettings.cbAsk.Checked) or ConfirmAction then
682
        begin
683
          targetTiles := TWorldItemList.Create(False);
684
          if SelectedTile = targetTile then
685
          begin
686
            targetTiles.Add(targetTile)
687
          end else
688
          begin
689
            blockInfo := nil;
690
            while FScreenBuffer.Iterate(blockInfo) do
691
            begin
692
              if (blockInfo^.State = ssNormal) and
693
                blockInfo^.Item.CanBeEdited and
694
                IsInRect(blockInfo^.Item.X, blockInfo^.Item.Y, targetRect) then
695
              begin
696
                targetTiles.Add(blockInfo^.Item);
697
              end;
698
            end;
699
          end;
700
701
          if acMove.Checked then                       //***** Move tile *****//
702
          begin
703
            offsetX := frmMoveSettings.GetOffsetX;
704
            offsetY := frmMoveSettings.GetOffsetY;
705
            for i := 0 to targetTiles.Count - 1 do
706
            begin
707
              tile := targetTiles.Items[i];
708
709
              if tile is TStaticItem then
710
              begin
711
                newX := EnsureRange(tile.X + offsetX, 0, FLandscape.CellWidth - 1);
712
                newY := EnsureRange(tile.Y + offsetY, 0, FLandscape.CellHeight - 1);
713
                FUndoList.Add(TMoveStaticPacket.Create(newX, newY, tile.Z,
714
                  tile.TileID, TStaticItem(tile).Hue, tile.X, tile.Y));
715
                dmNetwork.Send(TMoveStaticPacket.Create(TStaticItem(tile),
716
                  newX, newY));
717
              end;
718
            end;
719
          end else if acElevate.Checked then        //***** Elevate tile *****//
720
          begin
721
            for i := 0 to targetTiles.Count - 1 do
722
            begin
723
              tile := targetTiles.Items[i];
724
725
              z := frmElevateSettings.seZ.Value;
726
              if frmElevateSettings.rbRaise.Checked then
727
                z := EnsureRange(tile.Z + z, -128, 127)
728
              else if frmElevateSettings.rbLower.Checked then
729
                z := EnsureRange(tile.Z - z, -128, 127);
730
731
              if tile is TMapCell then
732
              begin
733
                if frmElevateSettings.cbRandomHeight.Checked then
734
                  Inc(z, Random(frmElevateSettings.seRandomHeight.Value));
735
                FUndoList.Add(TDrawMapPacket.Create(tile.X, tile.Y, tile.Z,
736
                  tile.TileID));
737
                dmNetwork.Send(TDrawMapPacket.Create(tile.X, tile.Y, z,
738
                  tile.TileID));
739
              end else
740
              begin
741
                FUndoList.Add(TElevateStaticPacket.Create(tile.X, tile.Y,
742
                  z, tile.TileID, TStaticItem(tile).Hue, tile.Z));
743
                dmNetwork.Send(TElevateStaticPacket.Create(TStaticItem(tile), z));
744
              end;
745
            end;
746
          end else if acDelete.Checked then          //***** Delete tile *****//
747
          begin
748
            for i := 0 to targetTiles.Count - 1 do
749
            begin
750
              tile := targetTiles.Items[i];
751
752
              if tile is TStaticItem then
753
              begin
754
                FUndoList.Add(TInsertStaticPacket.Create(tile.X, tile.Y,
755
                  tile.Z, tile.TileID, TStaticItem(tile).Hue));
756
                dmNetwork.Send(TDeleteStaticPacket.Create(TStaticItem(tile)));
757
              end;
758
            end;
759
          end else if acHue.Checked then                //***** Hue tile *****//
760
          begin
761
            for i := 0 to targetTiles.Count - 1 do
762
            begin
763
              tile := targetTiles.Items[i];
764
765
              if (tile is TStaticItem) and
766
                (TStaticItem(tile).Hue <> frmHueSettings.lbHue.ItemIndex) then
767
              begin
768
                FUndoList.Add(THueStaticPacket.Create(tile.X, tile.Y, tile.Z,
769
                  tile.TileID, frmHueSettings.lbHue.ItemIndex,
770
                  TStaticItem(tile).Hue));
771
                dmNetwork.Send(THueStaticPacket.Create(TStaticItem(tile),
772
                  frmHueSettings.lbHue.ItemIndex));
773
              end;
774
            end;
775
          end;
776
777
          targetTiles.Free;
778
        end;
779
      end;
780
    end;
781
  end;
782
  acUndo.Enabled := FUndoList.Count > 0;
783
  SelectedTile := nil;
784
  FRepaintNeeded := True;
785
  Logger.ExitMethod([lcClient, lcDebug], 'MouseUp');
786
end;
787
788
procedure TfrmMain.oglGameWindowMouseWheel(Sender: TObject; Shift: TShiftState;
789
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
790
var
791
  cursorNeedsUpdate: Boolean;
792
  newZ: ShortInt;
793
begin
794
  if CurrentTile = nil then
795
    Exit;
796
797
  //We want single steps ...
798
  WheelDelta := WheelDelta div WHEEL_DELTA;
799
800
  cursorNeedsUpdate := False;
801
  if (CurrentTile is TVirtualTile) or ((ssCtrl in Shift) and
802
     (frmVirtualLayer.cbShowLayer.Checked)) then
803
  begin
804
    frmVirtualLayer.seZ.Value := EnsureRange(frmVirtualLayer.seZ.Value +
805
      WheelDelta, -128, 127);
806
    frmVirtualLayer.seZChange(frmVirtualLayer.seZ);
807
    cursorNeedsUpdate := True;
808
    Handled := True;
809
  end else if not (ssCtrl in Shift) then
810
  begin
811
    FUndoList.Clear;
812
    newZ := EnsureRange(CurrentTile.Z + WheelDelta, -128, 127);
813
    if CurrentTile is TStaticItem then
814
    begin
815
      FUndoList.Add(TElevateStaticPacket.Create(CurrentTile.X, CurrentTile.Y,
816
        newZ, CurrentTile.TileID, TStaticItem(CurrentTile).Hue,
817
        CurrentTile.Z));
818
      dmNetwork.Send(TElevateStaticPacket.Create(TStaticItem(CurrentTile),
819
        newZ));
820
      cursorNeedsUpdate := True;
821
      Handled := True;
822
    end else if CurrentTile is TMapCell then
823
    begin
824
      FUndoList.Add(TDrawMapPacket.Create(CurrentTile.X, CurrentTile.Y,
825
        CurrentTile.Z, CurrentTile.TileID));
826
      dmNetwork.Send(TDrawMapPacket.Create(CurrentTile.X, CurrentTile.Y,
827
        newZ, CurrentTile.TileID));
828
      Handled := True;
829
    end;
830
    acUndo.Enabled := FUndoList.Count > 0;
831
  end;
832
  
833
  if cursorNeedsUpdate then
834
  begin
835
    SetCursorPos(Mouse.CursorPos.X, Mouse.CursorPos.Y - 4 * WheelDelta);
836
    UpdateCurrentTile(MousePos.X, MousePos.Y - 4 * WheelDelta);
837
  end;
838
839
  FRepaintNeeded := True;
840
end;
841
842
procedure TfrmMain.FormCreate(Sender: TObject);
843
begin
844
  FAppDir := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName));
845
  FConfigDir := GetAppConfigDir(False);
846
  ForceDirectories(FConfigDir);
847
848
  XMLPropStorage1.FileName := FConfigDir + 'CentrED.xml';
849
  XMLPropStorage1.Active := True;
850
851
  FLandscape := ResMan.Landscape;
852
  FLandscape.OnChange := @OnLandscapeChanged;
853
  FLandscape.OnMapChanged := @OnMapChanged;
854
  FLandscape.OnNewBlock := @OnNewBlock;
855
  FLandscape.OnStaticDeleted := @OnStaticDeleted;
856
  FLandscape.OnStaticElevated := @OnStaticElevated;
857
  FLandscape.OnStaticHued := @OnStaticHued;
858
  FLandscape.OnStaticInserted := @OnStaticInserted;
859
860
  FTextureManager := TLandTextureManager.Create;
861
  FScreenBuffer := TScreenBuffer.Create;
862
  FScreenBufferState := [];
863
  X := 0;
864
  Y := 0;
865
  edX.MaxValue := FLandscape.CellWidth;
866
  edY.MaxValue := FLandscape.CellHeight;
867
  FOverlayUI := TOverlayUI.Create;
868
  
869
  ProcessAccessLevel;
870
  
871
  vdtTiles.NodeDataSize := SizeOf(TTileInfo);
872
  vdtRandom.NodeDataSize := SizeOf(TTileInfo);
873
  BuildTileList;
874
  Randomize;
875
  
876
  vstChat.NodeDataSize := SizeOf(TChatInfo);
877
  pnlChatHeader.AnchorSide[akBottom].Control := pnlBottom;
878
  
879
  FLocationsFile := FConfigDir + 'Locations.xml';
880
  vstLocations.NodeDataSize := SizeOf(TLocationInfo);
881
  LoadLocations;
882
883
  RegisterPacketHandler($0C, TPacketHandler.Create(0, @OnClientHandlingPacket));
884
885
  FVLayerImage := TSingleImage.CreateFromStream(ResourceManager.GetResource(2));
886
887
  FGLFont := TGLFont.Create;
888
  FGLFont.LoadImage(ResourceManager.GetResource(3));
889
  FGLFont.LoadFontInfo(ResourceManager.GetResource(4));
890
891
  FVirtualTiles := TWorldItemList.Create(True);
892
  FUndoList := TPacketList.Create(True);
893
894
  FRandomPresetsFile := FConfigDir + 'RandomPresets.xml';
895
  LoadRandomPresets;
896
897
  DoubleBuffered := True;
898
  pnlBottom.DoubleBuffered := True;
899
  
900
  FLastDraw := Now;
901
end;
902
903
procedure TfrmMain.btnGoToClick(Sender: TObject);
904
begin
905
  SetPos(edX.Value, edY.Value);
906
end;
907
908
procedure TfrmMain.btnRandomPresetDeleteClick(Sender: TObject);
909
var
910
  preset: TDOMElement;
911
begin
912
  if cbRandomPreset.ItemIndex > -1 then
913
  begin
914
    preset := TDOMElement(cbRandomPreset.Items.Objects[cbRandomPreset.ItemIndex]);
915
    FRandomPresetsDoc.DocumentElement.RemoveChild(preset);
916
    cbRandomPreset.Items.Delete(cbRandomPreset.ItemIndex);
917
    cbRandomPreset.ItemIndex := -1;
918
  end;
919
end;
920
921
procedure TfrmMain.btnRandomPresetSaveClick(Sender: TObject);
922
var
923
  presetName: string;
924
  i: Integer;
925
  preset, tile: TDOMElement;
926
  children: TDOMNodeList;
927
  tileNode: PVirtualNode;
928
  tileInfo: PTileInfo;
929
begin
930
  presetName := cbRandomPreset.Text;
931
  if InputQuery('Save Preset', 'Enter the name of the preset:', presetName) then
932
  begin
933
    preset := FindRandomPreset(presetName);
934
    if preset = nil then
935
    begin
936
      preset := FRandomPresetsDoc.CreateElement('Preset');
937
      preset.AttribStrings['Name'] := presetName;
938
      FRandomPresetsDoc.DocumentElement.AppendChild(preset);
939
      cbRandomPreset.Items.AddObject(presetName, preset);
940
    end else
941
    begin
942
      children := preset.ChildNodes;
943
      for i := children.Count - 1 downto 0 do
944
        preset.RemoveChild(children[i]);
945
    end;
946
947
    tileNode := vdtRandom.GetFirst;
948
    while tileNode <> nil do
949
    begin
950
      tileInfo := vdtRandom.GetNodeData(tileNode);
951
      tile := FRandomPresetsDoc.CreateElement('Tile');
952
      tile.AttribStrings['ID'] := IntToStr(tileInfo^.ID);
953
      preset.AppendChild(tile);
954
      tileNode := vdtRandom.GetNext(tileNode);
955
    end;
956
957
    cbRandomPreset.ItemIndex := cbRandomPreset.Items.IndexOfObject(preset);
958
  end;
959
end;
960
961
procedure TfrmMain.cbRandomPresetChange(Sender: TObject);
962
var
963
  preset, tile: TDOMElement;
964
  tiles: TDOMNodeList;
965
  tileNode: PVirtualNode;
966
  tileInfo: PTileInfo;
967
  i: Integer;
968
begin
969
  if cbRandomPreset.ItemIndex > -1 then
970
  begin
971
    vdtRandom.Clear;
972
    preset := TDOMElement(cbRandomPreset.Items.Objects[cbRandomPreset.ItemIndex]);
973
    tiles := preset.ChildNodes;
974
    for i := 0 to tiles.Count - 1 do
975
    begin
976
      tile := TDOMElement(tiles[i]);
977
      if tile.NodeName = 'Tile' then
978
      begin
979
        tileNode := vdtRandom.AddChild(nil);
980
        tileInfo := vdtRandom.GetNodeData(tileNode);
981
        tileInfo^.ID := StrToInt(tile.AttribStrings['ID']);
982
      end;
983
    end;
984
  end;
985
end;
986
987
procedure TfrmMain.btnAddRandomClick(Sender: TObject);
988
var
989
  selected, node: PVirtualNode;
990
  sourceTileInfo, targetTileInfo: PTileInfo;
991
begin
992
  vdtRandom.BeginUpdate;
993
  selected := vdtTiles.GetFirstSelected;
994
  while selected <> nil do
995
  begin
996
    sourceTileInfo := vdtTiles.GetNodeData(selected);
997
    node := vdtRandom.AddChild(nil);
998
    targetTileInfo := vdtRandom.GetNodeData(node);
999
    targetTileInfo^.ID := sourceTileInfo^.ID;
1000
    selected := vdtTiles.GetNextSelected(selected);
1001
  end;
1002
  vdtRandom.EndUpdate;
1003
end;
1004
1005
procedure TfrmMain.btnClearLocationsClick(Sender: TObject);
1006
begin
1007
  if MessageDlg('Are you sure you want to delete all saved locations?',
1008
    mtConfirmation, [mbYes, mbNo], 0) = mrYes then
1009
  begin
1010
    vstLocations.Clear;
1011
  end;
1012
end;
1013
1014
procedure TfrmMain.ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);
1015
begin
1016
  if (FScreenBufferState <> CScreenBufferValid) or
1017
     ((FRepaintNeeded or mnuShowAnimations.Checked) and
1018
      (MilliSecondsBetween(Now, FLastDraw) > 50)) then
1019
  begin
1020
    //Logger.Send([lcClient, lcDebug], 'Repainting Game Window');
1021
    oglGameWindow.Repaint;
1022
    FLastDraw := Now;
1023
    FRepaintNeeded := False;
1024
  end;
1025
  Sleep(1);
1026
  Done := False;
1027
end;
1028
1029
procedure TfrmMain.btnAddLocationClick(Sender: TObject);
1030
var
1031
  locationName: string;
1032
  locationInfo: PLocationInfo;
1033
begin
1034
  locationName := '';
1035
  if InputQuery('New Location', 'Enter the name of the new location:',
1036
    locationName) then
1037
  begin
1038
    locationInfo := vstLocations.GetNodeData(vstLocations.AddChild(nil));
1039
    locationInfo^.X := X;
1040
    locationInfo^.Y := Y;
1041
    locationInfo^.Name := locationName;
1042
  end;
1043
end;
1044
1045
procedure TfrmMain.acSelectExecute(Sender: TObject);
1046
begin
1047
  acSelect.Checked := True;
1048
  tbSelect.Down := True;
1049
  mnuSelect.Checked := True;
1050
  ProcessToolState;
1051
end;
1052
1053
procedure TfrmMain.acUndoExecute(Sender: TObject);
1054
var
1055
  i: Integer;
1056
begin
1057
  for i := FUndoList.Count - 1 downto 0 do
1058
  begin
1059
    dmNetwork.Send(FUndoList[i]);
1060
    FUndoList[i] := nil;
1061
  end;
1062
  FUndoList.Clear;
1063
  acUndo.Enabled := False;
1064
end;
1065
1066
procedure TfrmMain.acVirtualLayerExecute(Sender: TObject);
1067
begin
1068
  frmVirtualLayer.Show;
1069
end;
1070
1071
procedure TfrmMain.acDrawExecute(Sender: TObject);
1072
begin
1073
  acDraw.Checked := True;
1074
  tbDrawTile.Down := True;
1075
  mnuDraw.Checked := True;
1076
  frmDrawSettings.ShowModal;
1077
  ProcessToolState;
1078
end;
1079
1080
procedure TfrmMain.acDeleteExecute(Sender: TObject);
1081
begin
1082
  acDelete.Checked := True;
1083
  tbDeleteTile.Down := True;
1084
  mnuDelete.Checked := True;
1085
  ProcessToolState;
1086
end;
1087
1088
procedure TfrmMain.acBoundariesExecute(Sender: TObject);
1089
begin
1090
  frmBoundaries.Show;
1091
end;
1092
1093
procedure TfrmMain.acElevateExecute(Sender: TObject);
1094
begin
1095
  acElevate.Checked := True;
1096
  tbElevateTile.Down := True;
1097
  mnuElevate.Checked := True;
1098
  ProcessToolState;
1099
  frmElevateSettings.Show;
1100
end;
1101
1102
procedure TfrmMain.acFilterExecute(Sender: TObject);
1103
begin
1104
  if acFilter.Checked then
1105
  begin
1106
    frmFilter.Show;
1107
    frmFilter.Locked := False;
1108
  end else
1109
    frmFilter.Hide;
1110
  InvalidateFilter;
1111
end;
1112
1113
procedure TfrmMain.acFlatExecute(Sender: TObject);
1114
begin
1115
  acFlat.Checked := not acFlat.Checked;
1116
  RebuildScreenBuffer;
1117
end;
1118
1119
procedure TfrmMain.acHueExecute(Sender: TObject);
1120
begin
1121
  acHue.Checked := True;
1122
  tbSetHue.Down := True;
1123
  mnuSetHue.Checked := True;
1124
  ProcessToolState;
1125
  frmHueSettings.Show;
1126
end;
1127
1128
procedure TfrmMain.acMoveExecute(Sender: TObject);
1129
begin
1130
  acMove.Checked := True;
1131
  tbMoveTile.Down := True;
1132
  mnuMove.Checked := True;
1133
  ProcessToolState;
1134
  frmMoveSettings.Show;
1135
end;
1136
1137
procedure TfrmMain.acNoDrawExecute(Sender: TObject);
1138
begin
1139
  acNoDraw.Checked := not acNoDraw.Checked;
1140
  RebuildScreenBuffer;
1141
end;
1142
1143
procedure TfrmMain.btnClearRandomClick(Sender: TObject);
1144
begin
1145
  vdtRandom.BeginUpdate;
1146
  vdtRandom.Clear;
1147
  vdtRandom.EndUpdate;
1148
end;
1149
1150
procedure TfrmMain.btnDeleteLocationClick(Sender: TObject);
1151
begin
1152
  vstLocations.DeleteSelectedNodes;
1153
end;
1154
1155
procedure TfrmMain.btnDeleteRandomClick(Sender: TObject);
1156
begin
1157
  vdtRandom.BeginUpdate;
1158
  vdtRandom.DeleteSelectedNodes;
1159
  vdtRandom.EndUpdate;
1160
end;
1161
1162
procedure TfrmMain.cbStaticsChange(Sender: TObject);
1163
begin
1164
  if (not cbStatics.Checked) and (not cbTerrain.Checked) then
1165
    cbTerrain.Checked := True;
1166
  BuildTileList;
1167
end;
1168
1169
procedure TfrmMain.cbTerrainChange(Sender: TObject);
1170
begin
1171
  if (not cbTerrain.Checked) and (not cbStatics.Checked) then
1172
    cbStatics.Checked := True;
1173
  BuildTileList;
1174
end;
1175
1176
procedure TfrmMain.edChatKeyPress(Sender: TObject; var Key: char);
1177
begin
1178
  if Key = #13 then
1179
  begin
1180
    Key := #0;
1181
    if edChat.Text <> '' then
1182
    begin
1183
      dmNetwork.Send(TChatMessagePacket.Create(edChat.Text));
1184
      edChat.Text := '';
1185
    end;
1186
  end;
1187
end;
1188
1189
procedure TfrmMain.edFilterEditingDone(Sender: TObject);
1190
begin
1191
  BuildTileList;
1192
end;
1193
1194
procedure TfrmMain.FormActivate(Sender: TObject);
1195
begin
1196
  if oglGameWindow.MouseEntered then
1197
    oglGameWindowMouseEnter(Sender);
1198
end;
1199
1200
procedure TfrmMain.FormClose(Sender: TObject; var CloseAction: TCloseAction);
1201
begin
1202
  dmNetwork.CheckClose(Self);
1203
end;
1204
1205
procedure TfrmMain.FormDestroy(Sender: TObject);
1206
begin
1207
  CurrentTile := nil;
1208
  SelectedTile := nil;
1209
1210
  SaveLocations;
1211
  SaveRandomPresets;
1212
1213
  FreeAndNil(FTextureManager);
1214
  FreeAndNil(FScreenBuffer);
1215
  FreeAndNil(FOverlayUI);
1216
  FreeAndNil(FVLayerImage);
1217
  FreeAndNil(FVLayerMaterial);
1218
  FreeAndNil(FVirtualTiles);
1219
  FreeAndNil(FUndoList);
1220
  FreeAndNil(FGLFont);
1221
  FreeAndNil(FRandomPresetsDoc);
1222
  
1223
  RegisterPacketHandler($0C, nil);
1224
end;
1225
1226
procedure TfrmMain.edSearchIDExit(Sender: TObject);
1227
begin
1228
  edSearchID.Visible := False;
1229
  edSearchID.Text := '';
1230
  //edSearchID.Font.Color := clWindowText;
1231
end;
1232
1233
procedure TfrmMain.edSearchIDKeyPress(Sender: TObject; var Key: char);
1234
var
1235
  enteredText: String;
1236
  tileID: Integer;
1237
  tileType: Char;
1238
  node: PVirtualNode;
1239
  tileInfo: PTileInfo;
1240
begin
1241
  if Key = #13 then
1242
  begin
1243
    Key := #0;
1244
    enteredText := edSearchID.Text;
1245
    tileType := #0;
1246
    if Length(enteredText) > 1 then
1247
      tileType := enteredText[Length(enteredText)];
1248
1249
    if not (tileType in ['S', 'T']) then
1250
    begin
1251
      if cbTerrain.Checked then
1252
        tileType := 'T'
1253
      else
1254
        tileType := 'S';
1255
    end else
1256
      Delete(enteredText, Length(enteredText), 1);
1257
    
1258
    tileID := 0;
1259
    if not TryStrToInt(enteredText, tileID) then
1260
    begin
1261
      //edSearchID.Font.Color := clRed;
1262
      MessageDlg('Error', 'The specified TileID is invalid.', mtError, [mbOK], 0);
1263
      vdtTiles.SetFocus;
1264
      Exit;
1265
    end;
1266
    
1267
    if tileType = 'S' then
1268
      Inc(tileID, $4000);
1269
      
1270
    node := vdtTiles.GetFirst;
1271
    while node <> nil do
1272
    begin
1273
      tileInfo := vdtTiles.GetNodeData(node);
1274
      if tileInfo^.ID = tileID then
1275
      begin
1276
        vdtTiles.ClearSelection;
1277
        vdtTiles.Selected[node] := True;
1278
        vdtTiles.FocusedNode := node;
1279
        Break;
1280
      end;
1281
      node := vdtTiles.GetNext(node);
1282
    end;
1283
    
1284
    if node = nil then
1285
    begin
1286
      //edSearchID.Font.Color := clRed;
1287
      MessageDlg('Error', 'The tile with the specified ID could not be found.' +
1288
        LineEnding + 'Check for conflicting filter settings.', mtError, [mbOK], 0);
1289
      vdtTiles.SetFocus;
1290
      Exit;
1291
    end;
1292
    //edSearchID.Font.Color := clWindowText;
1293
    edSearchID.Visible := False;
1294
  end else if Key = #27 then
1295
  begin
1296
    edSearchID.Visible := False;
1297
    //edSearchID.Font.Color := clWindowText;
1298
    Key := #0;
1299
  end else if not (Key in ['$', '0'..'9', 'a'..'f', 'A'..'F', 's', 'S',
1300
    't', 'T', #8]) then
1301
    Key := #0;
1302
end;
1303
1304
procedure TfrmMain.lblChatHeaderCaptionClick(Sender: TObject);
1305
begin
1306
  if pnlChat.Visible then
1307
  begin
1308
    pnlChat.Visible := False;
1309
    spChat.Visible := False;
1310
    pnlChatHeader.AnchorSide[akBottom].Control := pnlBottom;
1311
  end else
1312
  begin
1313
    spChat.Visible := True;
1314
    pnlChat.Visible := True;
1315
    spChat.Top := pnlChat.Top - spChat.Height;
1316
    pnlChatHeader.AnchorSide[akBottom].Control := spChat;
1317
    
1318
    lblChatHeaderCaption.Font.Bold := False;
1319
    lblChatHeaderCaption.Font.Italic := False;
1320
    lblChatHeaderCaption.Font.Color := clWindowText;
1321
    
1322
    edChat.SetFocus;
1323
  end;
1324
end;
1325
1326
procedure TfrmMain.lblChatHeaderCaptionMouseEnter(Sender: TObject);
1327
begin
1328
  lblChatHeaderCaption.Font.Underline := True;
1329
end;
1330
1331
procedure TfrmMain.lblChatHeaderCaptionMouseLeave(Sender: TObject);
1332
begin
1333
  lblChatHeaderCaption.Font.Underline := False;
1334
end;
1335
1336
procedure TfrmMain.mnuAboutClick(Sender: TObject);
1337
begin
1338
  frmAbout.ShowModal;
1339
end;
1340
1341
procedure TfrmMain.mnuAccountControlClick(Sender: TObject);
1342
begin
1343
  frmAccountControl.Show;
1344
end;
1345
1346
procedure TfrmMain.mnuDisconnectClick(Sender: TObject);
1347
begin
1348
  dmNetwork.Disconnect;
1349
end;
1350
1351
procedure TfrmMain.oglGameWindowPaint(Sender: TObject);
1352
begin
1353
  glClear(GL_COLOR_BUFFER_BIT);
1354
1355
  InitRender;
1356
  InitSize;
1357
1358
  glDisable(GL_DEPTH_TEST);
1359
  Render;
1360
1361
  oglGameWindow.SwapBuffers;
1362
end;
1363
1364
procedure TfrmMain.oglGameWindowResize(Sender: TObject);
1365
begin
1366
  InvalidateScreenBuffer;
1367
end;
1368
1369
procedure TfrmMain.pmGrabTileInfoPopup(Sender: TObject);
1370
begin
1371
  mnuGrabHue.Enabled := CurrentTile is TStaticItem;
1372
end;
1373
1374
procedure TfrmMain.tbFilterMouseMove(Sender: TObject; Shift: TShiftState; X,
1375
  Y: Integer);
1376
begin
1377
  if acFilter.Checked and (not frmFilter.Visible) then
1378
    frmFilter.Show;
1379
end;
1380
1381
procedure TfrmMain.tbRadarMapClick(Sender: TObject);
1382
begin
1383
  frmRadarMap.Show;
1384
  frmRadarMap.BringToFront;
1385
end;
1386
1387
procedure TfrmMain.tbStaticsClick(Sender: TObject);
1388
begin
1389
  RebuildScreenBuffer;
1390
end;
1391
1392
procedure TfrmMain.tbTerrainClick(Sender: TObject);
1393
begin
1394
  RebuildScreenBuffer;
1395
end;
1396
1397
procedure TfrmMain.tmGrabTileInfoTimer(Sender: TObject);
1398
begin
1399
  tmGrabTileInfo.Enabled := False;
1400
  if CurrentTile <> nil then
1401
    pmGrabTileInfo.PopUp(Mouse.CursorPos.X, Mouse.CursorPos.Y);
1402
    
1403
  SelectedTile := nil;
1404
end;
1405
1406
procedure TfrmMain.tmMovementTimer(Sender: TObject);
1407
begin
1408
  case FOverlayUI.ActiveArrow of
1409
    0: MoveBy(-8, 0);
1410
    1: MoveBy(-8, -8);
1411
    2: MoveBy(0, -8);
1412
    3: MoveBy(8, -8);
1413
    4: MoveBy(8, 0);
1414
    5: MoveBy(8, 8);
1415
    6: MoveBy(0, 8);
1416
    7: MoveBy(-8, 8);
1417
  end;
1418
end;
1419
1420
procedure TfrmMain.tmTileHintTimer(Sender: TObject);
1421
begin
1422
  frmTileInfo.Show;
1423
  tmTileHint.Enabled := False;
1424
end;
1425
1426
procedure TfrmMain.vdtRandomClick(Sender: TObject);
1427
var
1428
  node: PVirtualNode;
1429
  tileInfo: PTileInfo;
1430
  selectedID: Integer;
1431
begin
1432
  if vdtRandom.SelectedCount = 1 then
1433
  begin
1434
    node := vdtRandom.GetFirstSelected;
1435
    if node <> nil then
1436
    begin
1437
      tileInfo := vdtRandom.GetNodeData(node);
1438
      selectedID := tileInfo^.ID;
1439
1440
      node := vdtTiles.GetFirst;
1441
      while node <> nil do
1442
      begin
1443
        tileInfo := vdtTiles.GetNodeData(node);
1444
        if tileInfo^.ID = selectedID then
1445
        begin
1446
          vdtTiles.ClearSelection;
1447
          vdtTiles.Selected[node] := True;
1448
          vdtTiles.FocusedNode := node;
1449
          node := nil;
1450
        end else
1451
          node := vdtTiles.GetNext(node);
1452
      end;
1453
    end;
1454
  end;
1455
end;
1456
1457
procedure TfrmMain.vdtRandomDragDrop(Sender: TBaseVirtualTree; Source: TObject;
1458
  DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState;
1459
  Pt: TPoint; var Effect: Integer; Mode: TDropMode);
1460
begin
1461
  if Source = vdtTiles then
1462
    btnAddRandomClick(Sender);
1463
end;
1464
1465
procedure TfrmMain.vdtRandomDragOver(Sender: TBaseVirtualTree; Source: TObject;
1466
  Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode;
1467
  var Effect: Integer; var Accept: Boolean);
1468
begin
1469
  if source = vdtTiles then Accept := True;
1470
end;
1471
1472
procedure TfrmMain.vdtRandomLoadNode(Sender: TBaseVirtualTree;
1473
  Node: PVirtualNode; Stream: TStream);
1474
var
1475
  tileInfo: PTileInfo;
1476
begin
1477
  tileInfo := Sender.GetNodeData(Node);
1478
  Stream.Read(tileInfo^.ID, SizeOf(tileInfo^.ID));
1479
end;
1480
1481
procedure TfrmMain.vdtRandomSaveNode(Sender: TBaseVirtualTree;
1482
  Node: PVirtualNode; Stream: TStream);
1483
var
1484
  tileInfo: PTileInfo;
1485
begin
1486
  tileInfo := Sender.GetNodeData(Node);
1487
  Stream.Write(tileInfo^.ID, SizeOf(tileInfo^.ID));
1488
end;
1489
1490
procedure TfrmMain.vdtRandomUpdating(Sender: TBaseVirtualTree;
1491
  State: TVTUpdateState);
1492
begin
1493
  if acDraw.Checked then
1494
    ProcessToolState;
1495
end;
1496
1497
procedure TfrmMain.vdtTilesClick(Sender: TObject);
1498
begin
1499
  {if vdtTiles.GetFirstSelected <> nil then
1500
  begin
1501
    if not tbDrawTile.Down then
1502
    begin
1503
      frmDrawSettings.rbTileList.Checked := True;
1504
      tbDrawTileClick(Sender);
1505
    end else
1506
      ProcessToolState;
1507
  end;}
1508
  if acDraw.Checked then
1509
    ProcessToolState;
1510
end;
1511
1512
procedure TfrmMain.vdtTilesDrawNode(Sender: TBaseVirtualTree;
1513
  const PaintInfo: TVTPaintInfo);
1514
var
1515
  tileInfo: PTileInfo;
1516
  textStyle: TTextStyle;
1517
  artEntry: TArt;
1518
  tileData: TTileData;
1519
  id: Integer;
1520
begin
1521
  tileInfo := Sender.GetNodeData(PaintInfo.Node);
1522
  textStyle := PaintInfo.Canvas.TextStyle;
1523
  textStyle.Alignment := taCenter;
1524
  textStyle.Layout := tlCenter;
1525
  textStyle.Wordbreak := True;
1526
  case PaintInfo.Column of
1527
    0:
1528
      begin
1529
        id := tileInfo^.ID;
1530
        if id > $3FFF then
1531
          Dec(id, $4000);
1532
        PaintInfo.Canvas.TextRect(PaintInfo.CellRect, 0, 0, Format('$%x', [id]),
1533
          textStyle);
1534
      end;
1535
    1:
1536
      begin
1537
        if ResMan.Art.Exists(tileInfo^.ID) then
1538
        begin
1539
          artEntry := ResMan.Art.GetArt(tileInfo^.ID,
1540
            RGB2ARGB(PaintInfo.Canvas.Pixels[PaintInfo.CellRect.Left,
1541
              PaintInfo.CellRect.Top]), nil, False);
1542
          DisplayImage(PaintInfo.Canvas, PaintInfo.CellRect, artEntry.Graphic);
1543
          artEntry.Free;
1544
        end;
1545
      end;
1546
    2:
1547
      begin
1548
        tileData := TTileData(ResMan.Tiledata.Block[tileInfo^.ID]);
1549
        PaintInfo.Canvas.TextRect(PaintInfo.CellRect, PaintInfo.CellRect.Left,
1550
          PaintInfo.CellRect.Top, ISO_8859_1ToUTF8(Trim(tileData.TileName)),
1551
          textStyle);
1552
        tileData.Free;
1553
      end;
1554
  end;
1555
end;
1556
1557
procedure TfrmMain.vdtTilesEnter(Sender: TObject);
1558
begin
1559
  if acFilter.Checked and (not frmFilter.Visible) and (not frmFilter.Locked) then
1560
  begin
1561
    frmFilter.Locked := True;
1562
    frmFilter.Show;
1563
    frmMain.SetFocus;
1564
    frmFilter.Locked := False;
1565
  end;
1566
end;
1567
1568
procedure TfrmMain.vdtTilesExit(Sender: TObject);
1569
begin
1570
  {TODO : Fix mouse over on !Windows platforms}
1571
  {$IFDEF Windows}
1572
  tmTileHint.Enabled := False;
1573
  {$ENDIF Windows}
1574
end;
1575
1576
procedure TfrmMain.vdtTilesHotChange(Sender: TBaseVirtualTree; OldNode,
1577
  NewNode: PVirtualNode);
1578
{$IFDEF Windows}
1579
var
1580
  tileInfo: PTileInfo;
1581
{$ENDIF Windows}
1582
begin
1583
  {TODO : Fix mouse over on !Windows platforms}
1584
  {$IFDEF Windows}
1585
  if NewNode <> nil then
1586
  begin
1587
    tileInfo := vdtTiles.GetNodeData(NewNode);
1588
    frmTileInfo.Update(tileInfo^.ID);
1589
    tmTileHint.Enabled := True;
1590
  end else
1591
  begin
1592
    frmTileInfo.Hide;
1593
    tmTileHint.Enabled := False;
1594
  end;
1595
  {$ENDIF Windows}
1596
end;
1597
1598
procedure TfrmMain.vdtTilesKeyPress(Sender: TObject; var Key: char);
1599
begin
1600
  if Key in ['$', '0'..'9'] then
1601
  begin
1602
    edSearchID.Text := Key;
1603
    edSearchID.Visible := True;
1604
    edSearchID.SetFocus;
1605
    edSearchID.SelStart := 1;
1606
    Key := #0;
1607
  end;
1608
end;
1609
1610
procedure TfrmMain.vdtTilesMouseMove(Sender: TObject; Shift: TShiftState; X,
1611
  Y: Integer);
1612
begin
1613
  if tmTileHint.Enabled then
1614
  begin
1615
    tmTileHint.Enabled := False;
1616
    tmTileHint.Enabled := True; //Restart timer
1617
  end;
1618
  
1619
  if frmTileInfo.Visible then
1620
  begin
1621
    frmTileInfo.Hide;
1622
    tmTileHint.Enabled := True;
1623
  end;
1624
end;
1625
1626
procedure TfrmMain.vdtTilesScroll(Sender: TBaseVirtualTree; DeltaX,
1627
  DeltaY: Integer);
1628
begin
1629
  if Sender.CanFocus and Sender.MouseEntered then
1630
    Sender.SetFocus;
1631
end;
1632
1633
procedure TfrmMain.vstChatClick(Sender: TObject);
1634
begin
1635
  edChat.SetFocus;
1636
end;
1637
1638
procedure TfrmMain.vstChatFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
1639
var
1640
  chatInfo: PChatInfo;
1641
begin
1642
  chatInfo := Sender.GetNodeData(Node);
1643
  chatInfo^.Sender := '';
1644
  chatInfo^.Msg := '';
1645
end;
1646
1647
procedure TfrmMain.vstChatGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
1648
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
1649
var
1650
  chatInfo: PChatInfo;
1651
begin
1652
  chatInfo := Sender.GetNodeData(Node);
1653
  case Column of
1654
    0: CellText := TimeToStr(chatInfo^.Time);
1655
    1: CellText := chatInfo^.Sender;
1656
    2: CellText := chatInfo^.Msg;
1657
  end;
1658
end;
1659
1660
procedure TfrmMain.vstChatPaintText(Sender: TBaseVirtualTree;
1661
  const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
1662
  TextType: TVSTTextType);
1663
var
1664
  chatInfo: PChatInfo;
1665
begin
1666
  chatInfo := Sender.GetNodeData(Node);
1667
  if chatInfo^.Sender = 'System' then
1668
  begin
1669
    if Column = 1 then
1670
      TargetCanvas.Font.Style := TargetCanvas.Font.Style + [fsItalic, fsBold]
1671
    else
1672
      TargetCanvas.Font.Style := TargetCanvas.Font.Style + [fsItalic];
1673
  end;
1674
end;
1675
1676
procedure TfrmMain.vstLocationsDblClick(Sender: TObject);
1677
var
1678
  node: PVirtualNode;
1679
  locationInfo: PLocationInfo;
1680
begin
1681
  node := vstLocations.GetFirstSelected;
1682
  if node <> nil then
1683
  begin
1684
    locationInfo := vstLocations.GetNodeData(node);
1685
    SetPos(locationInfo^.X, locationInfo^.Y);
1686
  end;
1687
end;
1688
1689
procedure TfrmMain.vstLocationsFreeNode(Sender: TBaseVirtualTree;
1690
  Node: PVirtualNode);
1691
var
1692
  locationInfo: PLocationInfo;
1693
begin
1694
  locationInfo := Sender.GetNodeData(Node);
1695
  locationInfo^.Name := EmptyStr;
1696
end;
1697
1698
procedure TfrmMain.vstLocationsGetText(Sender: TBaseVirtualTree;
1699
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
1700
  var CellText: String);
1701
var
1702
  locationInfo: PLocationInfo;
1703
begin
1704
  locationInfo := Sender.GetNodeData(Node);
1705
  case Column of
1706
    0: CellText := Format('%d, %d', [locationInfo^.X, locationInfo^.Y]);
1707
    1: CellText := locationInfo^.Name;
1708
  end;
1709
end;
1710
1711
procedure TfrmMain.vstLocationsLoadNode(Sender: TBaseVirtualTree;
1712
  Node: PVirtualNode; Stream: TStream);
1713
var
1714
  locationInfo: PLocationInfo;
1715
  stringLength: Integer;
1716
  s: string;
1717
begin
1718
  locationInfo := Sender.GetNodeData(Node);
1719
  Stream.Read(locationInfo^.X, SizeOf(Word));
1720
  Stream.Read(locationInfo^.Y, SizeOf(Word));
1721
  stringLength := 0;
1722
  Stream.Read(stringLength, SizeOf(Integer));
1723
  SetLength(s, stringLength);
1724
  Stream.Read(s[1], stringLength);
1725
  locationInfo^.Name := s;
1726
end;
1727
1728
procedure TfrmMain.vstLocationsNewText(Sender: TBaseVirtualTree;
1729
  Node: PVirtualNode; Column: TColumnIndex; const NewText: String);
1730
var
1731
  locationInfo: PLocationInfo;
1732
begin
1733
  if Column = 1 then
1734
  begin
1735
    locationInfo := Sender.GetNodeData(Node);
1736
    locationInfo^.Name := NewText;
1737
  end;
1738
end;
1739
1740
procedure TfrmMain.vstLocationsSaveNode(Sender: TBaseVirtualTree;
1741
  Node: PVirtualNode; Stream: TStream);
1742
var
1743
  locationInfo: PLocationInfo;
1744
  stringLength: Integer;
1745
begin
1746
  locationInfo := Sender.GetNodeData(Node);
1747
  Stream.Write(locationInfo^.X, SizeOf(Word));
1748
  Stream.Write(locationInfo^.Y, SizeOf(Word));
1749
  stringLength := Length(locationInfo^.Name);
1750
  Stream.Write(stringLength, SizeOf(Integer));
1751
  Stream.Write(locationInfo^.Name[1], stringLength);
1752
end;
1753
1754
procedure TfrmMain.XMLPropStorage1RestoreProperties(Sender: TObject);
1755
begin
1756
  FTextureManager.UseAnims := mnuShowAnimations.Checked;
1757
end;
1758
1759
procedure TfrmMain.SetX(const AValue: Integer);
1760
begin
1761
  SetPos(AValue, FY);
1762
end;
1763
1764
procedure TfrmMain.SetY(const AValue: Integer);
1765
begin
1766
  SetPos(FX, AValue);
1767
end;
1768
1769
procedure TfrmMain.SetPos(AX, AY: Word);
1770
begin
1771
  if InRange(AX, 0, FLandscape.CellWidth - 1) and InRange(AY, 0,
1772
    FLandscape.CellHeight - 1) then
1773
  begin
1774
    FX := AX;
1775
    edX.Value := FX;
1776
    FY := AY;
1777
    edY.Value := FY;
1778
    dmNetwork.Send(TUpdateClientPosPacket.Create(AX, AY));
1779
    InvalidateScreenBuffer;
1780
    if frmRadarMap <> nil then frmRadarMap.Repaint;
1781
  end;
1782
end;
1783
1784
procedure TfrmMain.RegisterAccessChangedListener(
1785
  AListener: TAccessChangedListener);
1786
var
1787
  i: Integer;
1788
begin
1789
  for i := Low(FAccessChangedListeners) to High(FAccessChangedListeners) do
1790
    if FAccessChangedListeners[i] = AListener then
1791
      Exit; //Prevent duplicates
1792
  SetLength(FAccessChangedListeners, Length(FAccessChangedListeners) + 1);
1793
  FAccessChangedListeners[High(FAccessChangedListeners)] := AListener;
1794
end;
1795
1796
procedure TfrmMain.UnregisterAccessChangedListener(
1797
  AListener: TAccessChangedListener);
1798
var
1799
  i: Integer;
1800
  found: Boolean;
1801
begin
1802
  i := Low(FAccessChangedListeners);
1803
  found := False;
1804
  while (i <= High(FAccessChangedListeners)) and (not found) do
1805
  begin
1806
    if FAccessChangedListeners[i] = AListener then
1807
    begin
1808
      if i < High(FAccessChangedListeners) then
1809
        Move(FAccessChangedListeners[i+1], FAccessChangedListeners[i],
1810
          (High(FAccessChangedListeners) - Low(FAccessChangedListeners) - i) *
1811
          SizeOf(TAccessChangedListener)); //move subsequent entries
1812
      SetLength(FAccessChangedListeners, Length(FAccessChangedListeners) - 1);
1813
      found := True;
1814
    end else
1815
      Inc(i);
1816
  end;
1817
end;
1818
1819
procedure TfrmMain.SetCurrentTile(const AValue: TWorldItem);
1820
begin
1821
  Logger.EnterMethod([lcClient, lcDebug], 'SetCurrentTile');
1822
  if AValue = FCurrentTile then
1823
  begin
1824
    Logger.ExitMethod([lcClient, lcDebug], 'SetCurrentTile');
1825
    Exit;
1826
  end;
1827
  Logger.Send([lcClient, lcDebug], 'Value', AValue);
1828
1829
  if FCurrentTile <> nil then
1830
    FCurrentTile.OnDestroy.UnregisterEvent(@OnTileRemoved);
1831
  FCurrentTile := AValue;
1832
1833
  if FCurrentTile = nil then
1834
  begin
1835
    lblTileInfo.Caption := '';
1836
  end else
1837
  begin
1838
    FCurrentTile.OnDestroy.RegisterEvent(@OnTileRemoved);
1839
    if FCurrentTile is TVirtualTile then
1840
      lblTileInfo.Caption := Format('Virtual Layer: X: %d, Y: %d, Z: %d',
1841
        [FCurrentTile.X, FCurrentTile.Y, FCurrentTile.Z])
1842
    else if FCurrentTile is TMapCell then
1843
      lblTileInfo.Caption := Format('Terrain TileID: $%x, X: %d, Y: %d, Z: %d',
1844
        [FCurrentTile.TileID, FCurrentTile.X, FCurrentTile.Y, FCurrentTile.Z])
1845
    else if FCurrentTile is TStaticItem then
1846
      lblTileInfo.Caption := Format('Static TileID: $%x, X: %d, Y: %d, Z: %d, Hue: $%x',
1847
        [FCurrentTile.TileID, FCurrentTile.X, FCurrentTile.Y, FCurrentTile.Z,
1848
         TStaticItem(FCurrentTile).Hue]);
1849
  end;
1850
1851
  UpdateSelection;
1852
  Logger.ExitMethod([lcClient, lcDebug], 'SetCurrentTile');
1853
end;
1854
1855
procedure TfrmMain.SetSelectedTile(const AValue: TWorldItem);
1856
begin
1857
  Logger.EnterMethod([lcClient, lcDebug], 'SetSelectedTile');
1858
  if AValue = FSelectedTile then
1859
  begin
1860
    Logger.ExitMethod([lcClient, lcDebug], 'SetSelectedTile');
1861
    Exit;
1862
  end;
1863
  Logger.Send([lcClient, lcDebug], 'Value', AValue);
1864
1865
  if FSelectedTile <> nil then
1866
    FSelectedTile.OnDestroy.UnregisterEvent(@OnTileRemoved);
1867
  FSelectedTile := AValue;
1868
  if FSelectedTile <> nil then
1869
    FSelectedTile.OnDestroy.RegisterEvent(@OnTileRemoved);
1870
1871
  UpdateSelection;
1872
  Logger.ExitMethod([lcClient, lcDebug], 'SetSelectedTile');
1873
end;
1874
1875
procedure TfrmMain.SetNormalLights;
1876
const
1877
  specular: TGLArrayf4 = (2, 2, 2, 1);
1878
  ambient: TGLArrayf4 = (1, 1, 1, 1);
1879
begin
1880
  glLightfv(GL_LIGHT0, GL_AMBIENT, @specular);
1881
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @ambient);
1882
end;
1883
1884
procedure TfrmMain.SetDarkLights;
1885
const
1886
  specularDark: TGLArrayf4 = (0.5, 0.5, 0.5, 1);
1887
  ambientDark: TGLArrayf4 = (0.25, 0.25, 0.25, 1);
1888
begin
1889
  glLightfv(GL_LIGHT0, GL_AMBIENT, @specularDark);
1890
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, @ambientDark);
1891
end;
1892
1893
procedure TfrmMain.InitRender;
1894
const
1895
  lightPosition: TGLArrayf4 = (-1, -1, 0.5, 0);
1896
begin
1897
  glEnable(GL_ALPHA_TEST);
1898
  glAlphaFunc(GL_GREATER, 0.1);
1899
  glEnable(GL_TEXTURE_2D);
1900
  glDisable(GL_DITHER);
1901
  glEnable(GL_BLEND); // Enable alpha blending of textures
1902
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1903
  glShadeModel(GL_SMOOTH);
1904
  glEnable(GL_NORMALIZE);
1905
1906
  glEnable(GL_LIGHT0);
1907
  glLightfv(GL_LIGHT0, GL_POSITION, @lightPosition);
1908
  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
1909
end;
1910
1911
procedure TfrmMain.InitSize;
1912
begin
1913
  glViewport(0, 0, oglGameWindow.Width, oglGameWindow.Height);
1914
  glMatrixMode(GL_PROJECTION);
1915
  glLoadIdentity;
1916
  gluOrtho2D(0, oglGameWindow.Width, oglGameWindow.Height, 0);
1917
  glMatrixMode(GL_MODELVIEW);
1918
  glLoadIdentity;
1919
end;
1920
1921
procedure TfrmMain.LoadLocations;
1922
var
1923
  xmlDoc: TXMLDocument;
1924
  location: TDOMElement;
1925
  locationNode: PVirtualNode;
1926
  locationInfo: PLocationInfo;
1927
  locations: TDOMNodeList;
1928
  i, j: Integer;
1929
begin
1930
  vstLocations.Clear;
1931
1932
  if FileExists(FLocationsFile) then
1933
  begin
1934
    ReadXMLFile(xmlDoc, FLocationsFile);
1935
    if xmlDoc.DocumentElement.NodeName = 'Locations' then
1936
    begin
1937
      locations := xmlDoc.DocumentElement.ChildNodes;
1938
      for i := 0 to locations.Count - 1 do
1939
      begin
1940
        location := TDOMElement(locations[i]);
1941
        if location.NodeName = 'Location' then
1942
        begin
1943
          locationNode := vstLocations.AddChild(nil);
1944
          locationInfo := vstLocations.GetNodeData(locationNode);
1945
          locationInfo^.Name := location.AttribStrings['Name'];
1946
1947
          if TryStrToInt(location.AttribStrings['X'], j) then
1948
            locationInfo^.X := j
1949
          else
1950
            locationInfo^.X := 0;
1951
1952
          if TryStrToInt(location.AttribStrings['Y'], j) then
1953
            locationInfo^.Y := j
1954
          else
1955
            locationInfo^.Y := 0;
1956
        end;
1957
      end;
1958
    end;
1959
1960
    xmlDoc.Free;
1961
  end;
1962
end;
1963
1964
procedure TfrmMain.LoadRandomPresets;
1965
var
1966
  presets: TDOMNodeList;
1967
  i: Integer;
1968
begin
1969
  cbRandomPreset.Clear;
1970
1971
  FreeAndNil(FRandomPresetsDoc);
1972
  if FileExists(FRandomPresetsFile) then
1973
  begin
1974
    ReadXMLFile(FRandomPresetsDoc, FRandomPresetsFile);
1975
    presets := FRandomPresetsDoc.DocumentElement.ChildNodes;
1976
    for i := 0 to presets.Count - 1 do
1977
    begin
1978
      if presets[i].NodeName = 'Preset' then
1979
      begin
1980
        cbRandomPreset.Items.AddObject(TDOMElement(presets[i]).AttribStrings['Name'],
1981
          presets[i]);
1982
      end;
1983
    end;
1984
  end else
1985
  begin
1986
    FRandomPresetsDoc := TXMLDocument.Create;
1987
    FRandomPresetsDoc.AppendChild(FRandomPresetsDoc.CreateElement('RandomPresets'));
1988
  end;
1989
end;
1990
1991
procedure TfrmMain.MoveBy(AOffsetX, AOffsetY: Integer); inline;
1992
begin
1993
  SetPos(EnsureRange(FX + AOffsetX, 0, FLandscape.CellWidth - 1),
1994
         EnsureRange(FY + AOffsetY, 0, FLandscape.CellHeight - 1));
1995
  UpdateCurrentTile;
1996
end;
1997
1998
procedure TfrmMain.PrepareMapCell(AMapCell: TMapCell);
1999
var
2000
  current, north, east, west: PBlockInfo;
2001
  cell: TMapCell;
2002
begin
2003
  current := FScreenBuffer.UpdateSortOrder(AMapCell);
2004
  if current = nil then
2005
    Exit; //off-screen update
2006
2007
  PrepareScreenBlock(current);
2008
  Exclude(FScreenBufferState, sbsIndexed);
2009
2010
  //Find surrounding cells
2011
  current := nil;
2012
  north := nil;
2013
  east := nil;
2014
  west := nil;
2015
  while ((north = nil) or (east = nil) or (west = nil)) and
2016
    FScreenBuffer.Iterate(current) do
2017
  begin
2018
    if current^.Item is TMapCell then
2019
    begin
2020
      cell := TMapCell(current^.Item);
2021
      if (cell.X = AMapCell.X - 1) and (cell.Y = AMapCell.Y - 1) then
2022
        north := current
2023
      else if (cell.X = AMapCell.X) and (cell.Y = AMapCell.Y - 1) then
2024
        east := current
2025
      else if (cell.X = AMapCell.X - 1) and (cell.Y = AMapCell.Y) then
2026
        west := current;
2027
    end;
2028
  end;
2029
2030
  if north <> nil then PrepareScreenBlock(north);
2031
  if east <> nil then PrepareScreenBlock(east);
2032
  if west <> nil then PrepareScreenBlock(west);
2033
end;
2034
2035
procedure TfrmMain.InvalidateFilter;
2036
begin
2037
  Exclude(FScreenBufferState, sbsFiltered);
2038
end;
2039
2040
procedure TfrmMain.InvalidateScreenBuffer;
2041
begin
2042
  Exclude(FScreenBufferState, sbsValid);
2043
end;
2044
2045
procedure TfrmMain.PrepareScreenBlock(ABlockInfo: PBlockInfo);
2046
2047
  procedure GetLandAlt(const AX, AY: Integer; const ADefaultZ,
2048
    ADefaultRaw: SmallInt; out Z, RawZ: SmallInt);
2049
  var
2050
    cell: TMapCell;
2051
  begin
2052
    cell := FLandscape.MapCell[AX, AY];
2053
    if cell <> nil then
2054
    begin
2055
      Z := cell.Z;
2056
      RawZ := cell.RawZ;
2057
    end else
2058
    begin
2059
      Z := ADefaultZ;
2060
      RawZ := ADefaultRaw;
2061
    end;
2062
  end;
2063
2064
var
2065
  item: TWorldItem;
2066
  drawX, drawY: Integer;
2067
  z, west, south, east: SmallInt;
2068
  rawZ, rawWest, rawSouth, rawEast: SmallInt;
2069
  staticItem: TStaticItem;
2070
begin
2071
  //add normals to map tiles and materials where possible
2072
2073
  item := ABlockInfo^.Item;
2074
2075
  GetDrawOffset(item.X - FX, item.Y - FY, drawX, drawY);
2076
2077
  if acFlat.Checked then
2078
  begin
2079
    z := 0;
2080
    rawZ := 0;
2081
  end else
2082
  begin
2083
    z := item.Z;
2084
    rawZ := item.RawZ;
2085
  end;
2086
2087
  if ABlockInfo^.HighRes <> nil then ABlockInfo^.HighRes.DelRef;
2088
  if ABlockInfo^.LowRes <> nil then ABlockInfo^.LowRes.DelRef;
2089
2090
  ABlockInfo^.HighRes := nil;
2091
  ABlockInfo^.CheckRealQuad := False;
2092
  ABlockInfo^.Text.Free;
2093
2094
  if item is TMapCell then
2095
  begin
2096
    if not acFlat.Checked then
2097
    begin
2098
      GetLandAlt(item.X, item.Y + 1, z, rawZ, west, rawWest);
2099
      GetLandAlt(item.X + 1, item.Y + 1, z, rawZ, south, rawSouth);
2100
      GetLandAlt(item.X + 1, item.Y, z, rawZ, east, rawEast);
2101
2102
      if  (west <> z) or (south <> z) or (east <> z) then
2103
        ABlockInfo^.HighRes := FTextureManager.GetTexMaterial(item.TileID);
2104
2105
      if (rawWest <> rawZ) or (rawSouth <> rawZ) or (rawEast <> rawZ) then
2106
      begin
2107
        ABlockInfo^.RealQuad[0][0] := drawX;
2108
        ABlockInfo^.RealQuad[0][1] := drawY - rawZ * 4;
2109
        ABlockInfo^.RealQuad[1][0] := drawX + 22;
2110
        ABlockInfo^.RealQuad[1][1] := drawY + 22 - rawEast * 4;
2111
        ABlockInfo^.RealQuad[2][0] := drawX;
2112
        ABlockInfo^.RealQuad[2][1] := drawY + 44 - rawSouth * 4;
2113
        ABlockInfo^.RealQuad[3][0] := drawX - 22;
2114
        ABlockInfo^.RealQuad[3][1] := drawY + 22 - rawWest * 4;
2115
2116
        with ABlockInfo^ do
2117
        begin
2118
          with ScreenRect do
2119
          begin
2120
            Left := drawX - 22;
2121
            Right := drawX + 22;
2122
            Top := RealQuad[0][1];
2123
            Bottom := RealQuad[0][1];
2124
2125
            if RealQuad[1][1] < Top then Top := RealQuad[1][1];
2126
            if RealQuad[1][1] > Bottom then Bottom := RealQuad[1][1];
2127
2128
            if RealQuad[2][1] < Top then Top := RealQuad[2][1];
2129
            if RealQuad[2][1] > Bottom then Bottom := RealQuad[2][1];
2130
2131
            if RealQuad[3][1] < Top then Top := RealQuad[3][1];
2132
            if RealQuad[3][1] > Bottom then Bottom := RealQuad[3][1];
2133
          end;
2134
          CheckRealQuad := True;
2135
        end;
2136
      end;
2137
    end else
2138
    begin
2139
      if mnuFlatShowHeight.Checked then
2140
        ABlockInfo^.Text := TGLText.Create(FGLFont, IntToStr(item.Z));
2141
    end;
2142
2143
    if not ABlockInfo^.CheckRealQuad then
2144
      ABlockInfo^.ScreenRect := Bounds(Trunc(drawX - 22),
2145
        Trunc(drawY - rawZ * 4), 44, 44);
2146
2147
    ABlockInfo^.LowRes := FTextureManager.GetArtMaterial(item.TileID);
2148
2149
    if ABlockInfo^.HighRes <> nil then
2150
    begin
2151
      if ABlockInfo^.Normals = nil then
2152
        New(ABlockInfo^.Normals);
2153
      FLandscape.GetNormals(item.X, item.Y, ABlockInfo^.Normals^);
2154
      ABlockInfo^.DrawQuad[0][0] := drawX;
2155
      ABlockInfo^.DrawQuad[0][1] := drawY - z * 4;
2156
      ABlockInfo^.DrawQuad[1][0] := drawX + 22;
2157
      ABlockInfo^.DrawQuad[1][1] := drawY + 22 - east * 4;
2158
      ABlockInfo^.DrawQuad[2][0] := drawX;
2159
      ABlockInfo^.DrawQuad[2][1] := drawY + 44 - south * 4;
2160
      ABlockInfo^.DrawQuad[3][0] := drawX - 22;
2161
      ABlockInfo^.DrawQuad[3][1] := drawY + 22 - west * 4;
2162
    end else
2163
    begin
2164
      ABlockInfo^.DrawQuad[0][0] := drawX - 22;
2165
      ABlockInfo^.DrawQuad[0][1] := drawY - z * 4;
2166
      ABlockInfo^.DrawQuad[1][0] := drawX - 22 + ABlockInfo^.LowRes.Width;
2167
      ABlockInfo^.DrawQuad[1][1] := drawY - z * 4;
2168
      ABlockInfo^.DrawQuad[2][0] := drawX - 22 + ABlockInfo^.LowRes.Width;
2169
      ABlockInfo^.DrawQuad[2][1] := drawY + ABlockInfo^.LowRes.Height - z * 4;
2170
      ABlockInfo^.DrawQuad[3][0] := drawX - 22;
2171
      ABlockInfo^.DrawQuad[3][1] := drawY + ABlockInfo^.LowRes.Height - z * 4;
2172
    end;
2173
  end else
2174
  if item is TVirtualTile then
2175
  begin
2176
    ABlockInfo^.LowRes := FVLayerMaterial;
2177
    ABlockInfo^.LowRes.AddRef;
2178
    ABlockInfo^.ScreenRect := Bounds(Trunc(drawX - 22), Trunc(drawY - z * 4),
2179
      44, 44);
2180
    ABlockInfo^.DrawQuad[0][0] := drawX - 22;
2181
    ABlockInfo^.DrawQuad[0][1] := drawY - z * 4;
2182
    ABlockInfo^.DrawQuad[1][0] := drawX - 22 + ABlockInfo^.LowRes.Width;
2183
    ABlockInfo^.DrawQuad[1][1] := drawY - z * 4;
2184
    ABlockInfo^.DrawQuad[2][0] := drawX - 22 + ABlockInfo^.LowRes.Width;
2185
    ABlockInfo^.DrawQuad[2][1] := drawY + ABlockInfo^.LowRes.Height - z * 4;
2186
    ABlockInfo^.DrawQuad[3][0] := drawX - 22;
2187
    ABlockInfo^.DrawQuad[3][1] := drawY + ABlockInfo^.LowRes.Height - z * 4;
2188
  end else
2189
  begin
2190
    staticItem := TStaticItem(item);
2191
2192
    ABlockInfo^.LowRes := FTextureManager.GetStaticMaterial(staticItem);
2193
    ABlockInfo^.HueOverride := False;
2194
    ABlockInfo^.ScreenRect := Bounds(Trunc(drawX - ABlockInfo^.LowRes.RealWidth / 2),
2195
      Trunc(drawY + 44 - ABlockInfo^.LowRes.RealHeight - z * 4),
2196
      ABlockInfo^.LowRes.RealWidth,
2197
      ABlockInfo^.LowRes.RealHeight);
2198
2199
    ABlockInfo^.Translucent := tdfTranslucent in
2200
      ResMan.Tiledata.StaticTiles[staticItem.TileID].Flags;
2201
2202
    south := ABlockInfo^.LowRes.RealHeight;
2203
    east := ABlockInfo^.LowRes.RealWidth div 2;
2204
2205
    ABlockInfo^.DrawQuad[0][0] := drawX - east;
2206
    ABlockInfo^.DrawQuad[0][1] := drawY + 44 - south - z * 4;
2207
    ABlockInfo^.DrawQuad[1][0] := drawX - east + ABlockInfo^.LowRes.Width;
2208
    ABlockInfo^.DrawQuad[1][1] := drawY + 44 - south - z * 4;
2209
    ABlockInfo^.DrawQuad[2][0] := drawX - east + ABlockInfo^.LowRes.Width;
2210
    ABlockInfo^.DrawQuad[2][1] := drawY + 44 - south + ABlockInfo^.LowRes.Height - z * 4;
2211
    ABlockInfo^.DrawQuad[3][0] := drawX - east;
2212
    ABlockInfo^.DrawQuad[3][1] := drawY + 44 - south + ABlockInfo^.LowRes.Height - z * 4;
2213
  end;
2214
end;
2215
2216
procedure TfrmMain.Render;
2217
var
2218
  highlight: Boolean;
2219
  intensity: GLfloat;
2220
  blockInfo: PBlockInfo;
2221
  item: TWorldItem;
2222
begin
2223
  if not (sbsValid in FScreenBufferState) then
2224
    RebuildScreenBuffer;
2225
2226
  if not (sbsIndexed in FScreenBufferState) then
2227
  begin
2228
    FScreenBuffer.UpdateShortcuts;
2229
    Include(FScreenBufferState, sbsIndexed);
2230
  end;
2231
2232
  if not (sbsFiltered in FScreenBufferState) then
2233
    UpdateFilter;
2234
2235
  blockInfo := nil;
2236
  while FScreenBuffer.Iterate(blockInfo) do
2237
  begin
2238
    if blockInfo^.State = ssFiltered then
2239
      Continue;
2240
2241
    item := blockInfo^.Item;
2242
2243
    if acSelect.Checked or item.CanBeEdited or (item is TVirtualTile) then
2244
    begin
2245
      intensity := 1.0;
2246
      SetNormalLights;
2247
    end else
2248
    begin
2249
      intensity := 0.5;
2250
      SetDarkLights;
2251
    end;
2252
2253
    if blockInfo^.Translucent then
2254
      glColor4f(intensity, intensity, intensity, 0.5)
2255
    else
2256
      glColor4f(intensity, intensity, intensity, 1.0);
2257
2258
    highlight := blockInfo^.Highlighted and item.CanBeEdited;
2259
2260
    if highlight then
2261
    begin
2262
      glEnable(GL_COLOR_LOGIC_OP);
2263
      glLogicOp(GL_COPY_INVERTED);
2264
    end;
2265
2266
    if blockInfo^.HighRes <> nil then
2267
    begin
2268
      glBindTexture(GL_TEXTURE_2D, blockInfo^.HighRes.Texture);
2269
2270
      if not highlight then
2271
        glEnable(GL_LIGHTING);
2272
2273
      glBegin(GL_QUADS);
2274
        glNormal3fv(@blockInfo^.Normals^[0]);
2275
        glTexCoord2i(0, 0); glVertex2iv(@blockInfo^.DrawQuad[0]);
2276
        glNormal3fv(@blockInfo^.Normals^[3]);
2277
        glTexCoord2i(0, 1); glVertex2iv(@blockInfo^.DrawQuad[3]);
2278
        glNormal3fv(@blockInfo^.Normals^[2]);
2279
        glTexCoord2i(1, 1); glVertex2iv(@blockInfo^.DrawQuad[2]);
2280
        glNormal3fv(@blockInfo^.Normals^[1]);
2281
        glTexCoord2i(1, 0); glVertex2iv(@blockInfo^.DrawQuad[1]);
2282
      glEnd;
2283
2284
      if not highlight then
2285
        glDisable(GL_LIGHTING);
2286
    end else
2287
    begin
2288
      glBindTexture(GL_TEXTURE_2D, blockInfo^.LowRes.Texture);
2289
      glBegin(GL_QUADS);
2290
        glTexCoord2i(0, 0); glVertex2iv(@blockInfo^.DrawQuad[0]);
2291
        glTexCoord2i(1, 0); glVertex2iv(@blockInfo^.DrawQuad[1]);
2292
        glTexCoord2i(1, 1); glVertex2iv(@blockInfo^.DrawQuad[2]);
2293
        glTexCoord2i(0, 1); glVertex2iv(@blockInfo^.DrawQuad[3]);
2294
      glEnd;
2295
    end;
2296
2297
    if highlight then
2298
      glDisable(GL_COLOR_LOGIC_OP);
2299
2300
    if (blockInfo^.Text <> nil) then
2301
      blockInfo^.Text.Render(blockInfo^.ScreenRect);
2302
  end;
2303
2304
  FOverlayUI.Draw(oglGameWindow);
2305
end;
2306
2307
procedure TfrmMain.SaveLocations;
2308
var
2309
  xmlDoc: TXMLDocument;
2310
  location: TDOMElement;
2311
  locationNode: PVirtualNode;
2312
  locationInfo: PLocationInfo;
2313
begin
2314
  xmlDoc := TXMLDocument.Create;
2315
  xmlDoc.AppendChild(xmlDoc.CreateElement('Locations'));
2316
2317
  locationNode := vstLocations.GetFirst;
2318
  while locationNode <> nil do
2319
  begin
2320
    locationInfo := vstLocations.GetNodeData(locationNode);
2321
    location := xmlDoc.CreateElement('Location');
2322
    location.AttribStrings['Name'] := locationInfo^.Name;
2323
    location.AttribStrings['X'] := IntToStr(locationInfo^.X);
2324
    location.AttribStrings['Y'] := IntToStr(locationInfo^.Y);
2325
    xmlDoc.DocumentElement.AppendChild(location);
2326
2327
    locationNode := vstLocations.GetNext(locationNode);
2328
  end;
2329
2330
  WriteXMLFile(xmlDoc, FLocationsFile);
2331
  xmlDoc.Free;
2332
end;
2333
2334
procedure TfrmMain.SaveRandomPresets;
2335
begin
2336
  WriteXMLFile(FRandomPresetsDoc, FRandomPresetsFile);
2337
end;
2338
2339
procedure TfrmMain.OnLandscapeChanged;
2340
begin
2341
  InvalidateScreenBuffer;
2342
  oglGameWindow.Repaint;
2343
  UpdateCurrentTile;
2344
end;
2345
2346
procedure TfrmMain.OnMapChanged(AMapCell: TMapCell);
2347
begin
2348
  PrepareMapCell(AMapCell);
2349
  ForceUpdateCurrentTile;
2350
end;
2351
2352
procedure TfrmMain.OnNewBlock(ABlock: TBlock);
2353
begin
2354
  InvalidateScreenBuffer;
2355
end;
2356
2357
procedure TfrmMain.OnStaticDeleted(AStaticItem: TStaticItem);
2358
begin
2359
  FScreenBuffer.Delete(AStaticItem);
2360
  UpdateCurrentTile;
2361
  FRepaintNeeded := True;
2362
  ForceUpdateCurrentTile;
2363
end;
2364
2365
procedure TfrmMain.OnStaticElevated(AStaticItem: TStaticItem);
2366
var
2367
  blockInfo: PBlockInfo;
2368
begin
2369
  AStaticItem.PrioritySolver := FScreenBuffer.GetSerial;
2370
  blockInfo := FScreenBuffer.UpdateSortOrder(AStaticItem);
2371
  if blockInfo <> nil then
2372
  begin
2373
    PrepareScreenBlock(blockInfo);
2374
    Exclude(FScreenBufferState, sbsIndexed);
2375
2376
    ForceUpdateCurrentTile;
2377
  end;
2378
end;
2379
2380
procedure TfrmMain.OnStaticHued(AStaticItem: TStaticItem);
2381
var
2382
  blockInfo: PBlockInfo;
2383
begin
2384
  blockInfo := nil;
2385
  while FScreenBuffer.Iterate(blockInfo) do
2386
  begin
2387
    if blockInfo^.Item = AStaticItem then
2388
    begin
2389
      PrepareScreenBlock(blockInfo);
2390
      FRepaintNeeded := True;
2391
      Break;
2392
    end;
2393
  end;
2394
end;
2395
2396
procedure TfrmMain.OnStaticInserted(AStaticItem: TStaticItem);
2397
begin
2398
  if (AStaticItem.X >= FX + FLowOffsetX) and
2399
     (AStaticItem.X <= FX + FHighOffsetX) and
2400
     (AStaticItem.Y >= FY + FLowOffsetY) and
2401
     (AStaticItem.Y <= FY + FHighOffsetY) then
2402
  begin
2403
    AStaticItem.PrioritySolver := FScreenBuffer.GetSerial;
2404
    PrepareScreenBlock(FScreenBuffer.Insert(AStaticItem));
2405
    FRepaintNeeded := True;
2406
    ForceUpdateCurrentTile;
2407
  end;
2408
end;
2409
2410
procedure TfrmMain.BuildTileList;
2411
var
2412
  minID, maxID, i, lastID: Integer;
2413
  node: PVirtualNode;
2414
  tileInfo: PTileInfo;
2415
  filter: string;
2416
begin
2417
  if cbTerrain.Checked then minID := $0 else minID := $4000;
2418
  if cbStatics.Checked then maxID := $7FFF else maxID := $3FFF;
2419
  filter := AnsiLowerCase(UTF8ToISO_8859_1(edFilter.Text));
2420
  
2421
  node := vdtTiles.GetFirstSelected;
2422
  if node <> nil then
2423
  begin
2424
    tileInfo := vdtTiles.GetNodeData(node);
2425
    lastID := tileInfo^.ID;
2426
  end else
2427
    lastID := -1;
2428
  
2429
  vdtTiles.BeginUpdate;
2430
  vdtTiles.Clear;
2431
  
2432
  for i := minID to maxID do
2433
  begin
2434
    if ResMan.Art.Exists(i) then
2435
    begin
2436
      if (filter <> '') and (Pos(filter, AnsiLowerCase(
2437
        ResMan.Tiledata.TileData[i].TileName)) = 0) then Continue;
2438
      node := vdtTiles.AddChild(nil);
2439
      tileInfo := vdtTiles.GetNodeData(node);
2440
      tileInfo^.ID := i;
2441
      if i = lastID then
2442
        vdtTiles.Selected[node] := True;
2443
    end;
2444
  end;
2445
  
2446
  if vdtTiles.GetFirstSelected = nil then
2447
  begin
2448
    node := vdtTiles.GetFirst;
2449
    if node <> nil then
2450
      vdtTiles.Selected[node] := True;
2451
  end;
2452
  
2453
  vdtTiles.EndUpdate;
2454
  
2455
  node := vdtTiles.GetFirstSelected;
2456
  if node <> nil then
2457
    vdtTiles.FocusedNode := node;
2458
end;
2459
2460
procedure TfrmMain.ProcessToolState;
2461
var
2462
  blockInfo: PBlockInfo;
2463
begin
2464
  if acSelect.Checked then
2465
  begin
2466
    //lblTip.Caption := 'Right click shows a menu with all the tools.';
2467
    lblTip.Caption := 'Press and hold the left mouse button to show a list with'
2468
      + ' actions (eg. grab hue).';
2469
    oglGameWindow.Cursor := crDefault;
2470
2471
    //no highlighted tiles in "selection" mode
2472
    Logger.Send([lcClient, lcDebug], 'Disable highlighting');
2473
    blockInfo := nil;
2474
    while FScreenBuffer.Iterate(blockInfo) do
2475
      if blockInfo^.State = ssNormal then
2476
        blockInfo^.Highlighted := False;
2477
  end else
2478
  begin
2479
    lblTip.Caption := 'Press and hold the left mouse button to target an area.';
2480
    oglGameWindow.Cursor := crHandPoint;
2481
  end;
2482
2483
  FRepaintNeeded := True;
2484
end;
2485
2486
procedure TfrmMain.ProcessAccessLevel;
2487
begin
2488
  mnuAdministration.Visible := (dmNetwork.AccessLevel >= alAdministrator);
2489
  acDraw.Enabled := (dmNetwork.AccessLevel >= alNormal);
2490
  acMove.Enabled := (dmNetwork.AccessLevel >= alNormal);
2491
  acElevate.Enabled := (dmNetwork.AccessLevel >= alNormal);
2492
  acDelete.Enabled := (dmNetwork.AccessLevel >= alNormal);
2493
  acHue.Enabled := (dmNetwork.AccessLevel >= alNormal);
2494
  Caption := Format('UO CentrED - [%s (%s)]', [dmNetwork.Username, GetAccessLevelString(dmNetwork.AccessLevel)]);
2495
end;
2496
2497
procedure TfrmMain.RebuildScreenBuffer;
2498
var
2499
  blockInfo: PBlockInfo;
2500
  i, tileX, tileY: Integer;
2501
  virtualTile: TVirtualTile;
2502
begin
2503
  Logger.EnterMethod([lcClient], 'RebuildScreenBuffer');
2504
2505
  FDrawDistance := Trunc(Sqrt(oglGameWindow.Width * oglGameWindow.Width + oglGamewindow.Height * oglGamewindow.Height) / 44);
2506
  Logger.Send([lcClient], 'DrawDistance', FDrawDistance);
2507
2508
  {$HINTS off}{$WARNINGS off}
2509
  if FX - FDrawDistance < 0 then FLowOffsetX := -FX else FLowOffsetX := -FDrawDistance;
2510
  if FY - FDrawDistance < 0 then FLowOffsetY := -FY else FLowOffsetY := -FDrawDistance;
2511
  if FX + FDrawDistance >= FLandscape.Width * 8 then FHighOffsetX := FLandscape.Width * 8 - FX - 1 else FHighOffsetX := FDrawDistance;
2512
  if FY + FDrawDistance >= FLandscape.Height * 8 then FHighOffsetY := FLandscape.Height * 8 - FY - 1 else FHighOffsetY := FDrawDistance;
2513
  {$HINTS on}{$WARNINGS on}
2514
2515
  FRangeX := FHighOffsetX - FLowOffsetX;
2516
  FRangeY := FHighOffsetY - FLowOffsetY;
2517
2518
  FLandscape.PrepareBlocks((FX + FLowOffsetX) div 8, (FY + FLowOffsetY) div 8, (FX + FHighOffsetX) div 8 + 1, (FY + FHighOffsetY) div 8 + 1);
2519
2520
  if frmVirtualLayer.cbShowLayer.Checked then
2521
  begin
2522
    Logger.Send([lcClient, lcDebug], 'Preparing Virtual Layer');
2523
2524
    if FVLayerMaterial = nil then
2525
      FVLayerMaterial := TSimpleMaterial.Create(FVLayerImage);
2526
2527
    i := 0;
2528
    for tileX := FX + FLowOffsetX to FX + FHighOffsetX do
2529
    begin
2530
      for tileY := FY + FLowOffsetY to FY + FHighOffsetY do
2531
      begin
2532
        while (i < FVirtualTiles.Count) and (not (FVirtualTiles[i] is TVirtualTile)) do
2533
          Inc(i);
2534
2535
        if i < FVirtualTiles.Count then
2536
        begin
2537
          virtualTile := TVirtualTile(FVirtualTiles[i]);
2538
        end else
2539
        begin
2540
          virtualTile := TVirtualTile.Create(nil);
2541
          FVirtualTiles.Add(virtualTile);
2542
        end;
2543
2544
        virtualTile.X := tileX;
2545
        virtualTile.Y := tileY;
2546
        virtualTile.Z := frmVirtualLayer.seZ.Value;
2547
        virtualTile.Priority := virtualTile.Z;
2548
        virtualTile.PriorityBonus := High(ShortInt);
2549
2550
        Inc(i);
2551
      end;
2552
    end;
2553
    while i < FVirtualTiles.Count do
2554
    begin
2555
      if FVirtualTiles[i] is TVirtualTile then
2556
        FVirtualTiles.Delete(i)
2557
      else
2558
        Inc(i);
2559
    end;
2560
  end else
2561
  begin
2562
    for i := FVirtualTiles.Count - 1 downto 0 do
2563
      if FVirtualTiles[i] is TVirtualTile then
2564
        FVirtualTiles.Delete(i);
2565
  end;
2566
2567
  Logger.Send([lcClient, lcDebug], 'VirtualTiles', FVirtualTiles.Count);
2568
2569
  FLandscape.FillDrawList(FScreenBuffer, FX + FLowOffsetX, FY + FLowOffsetY,
2570
    FRangeX, FRangeY, tbTerrain.Down, tbStatics.Down, acNoDraw.Checked,
2571
    FVirtualTiles);
2572
2573
  //Pre-process the buffer
2574
  blockInfo := nil;
2575
  while FScreenBuffer.Iterate(blockInfo) do
2576
    PrepareScreenBlock(blockInfo);
2577
2578
  FScreenBuffer.UpdateShortcuts;
2579
  FScreenBufferState := [sbsValid, sbsIndexed];
2580
2581
  Logger.ExitMethod([lcClient], 'RebuildScreenBuffer');
2582
end;
2583
2584
procedure TfrmMain.UpdateCurrentTile;
2585
var
2586
  localPos: TPoint;
2587
begin
2588
  if oglGameWindow.MouseEntered then
2589
  begin
2590
    localPos := oglGameWindow.ScreenToClient(Mouse.CursorPos);
2591
    UpdateCurrentTile(localPos.X, localPos.Y);
2592
  end;
2593
end;
2594
2595
procedure TfrmMain.UpdateCurrentTile(AX, AY: Integer);
2596
var
2597
  blockInfo: PBlockInfo;
2598
begin
2599
  Logger.EnterMethod([lcClient, lcDebug], 'UpdateCurrentTile');
2600
  FOverlayUI.ActiveArrow := FOverlayUI.HitTest(AX, AY);
2601
  if FOverlayUI.ActiveArrow > -1 then
2602
  begin
2603
    Logger.Send([lcClient, lcDebug], 'Overlay active');
2604
    CurrentTile := nil;
2605
    Logger.ExitMethod([lcClient, lcDebug], 'UpdateCurrentTile');
2606
    Exit;
2607
  end;
2608
2609
  blockInfo := FScreenBuffer.Find(Point(AX, AY));
2610
  if blockInfo <> nil then
2611
    CurrentTile := blockInfo^.Item
2612
  else
2613
    CurrentTile := nil;
2614
2615
  Logger.ExitMethod([lcClient, lcDebug], 'UpdateCurrentTile');
2616
end;
2617
2618
procedure TfrmMain.UpdateFilter;
2619
var
2620
  blockInfo: PBlockInfo;
2621
begin
2622
  blockInfo := nil;
2623
  while FScreenBuffer.Iterate(blockInfo) do
2624
  begin
2625
    if blockInfo^.State in [ssNormal, ssFiltered] then
2626
    begin
2627
      blockInfo^.State := ssNormal;
2628
      if (blockInfo^.Item.Z < frmBoundaries.tbMinZ.Position) or
2629
        (blockInfo^.Item.Z > frmBoundaries.tbMaxZ.Position) then
2630
      begin
2631
        blockInfo^.State := ssFiltered;
2632
      end else
2633
      if tbFilter.Down and (blockInfo^.Item is TStaticItem) and
2634
        (not frmFilter.Filter(TStaticItem(blockInfo^.Item))) then
2635
      begin
2636
        blockInfo^.State := ssFiltered;
2637
      end;
2638
    end;
2639
  end;
2640
  Include(FScreenBufferState, sbsFiltered);
2641
end;
2642
2643
procedure TfrmMain.UpdateSelection;
2644
2645
  procedure SetHighlight(ABlockInfo: PBlockInfo; AHighlighted: Boolean);
2646
  begin
2647
    if (ABlockInfo^.Item is TStaticItem) and acHue.Checked then
2648
    begin
2649
      if ABlockInfo^.HueOverride <> AHighlighted then
2650
      begin
2651
        ABlockInfo^.HueOverride := AHighlighted;
2652
        if AHighlighted then
2653
          ABlockInfo^.LowRes := FTextureManager.GetStaticMaterial(
2654
            TStaticItem(ABlockInfo^.Item), frmHueSettings.lbHue.ItemIndex)
2655
        else
2656
          ABlockInfo^.LowRes := FTextureManager.GetStaticMaterial(
2657
            TStaticItem(ABlockInfo^.Item));
2658
      end;
2659
    end else
2660
    begin
2661
      ABlockInfo^.Highlighted := AHighlighted;
2662
    end;
2663
  end;
2664
2665
  procedure AddGhostTile(AX, AY: Word; ABaseTile: TWorldItem);
2666
  var
2667
    blockInfo: PBlockInfo;
2668
    tileInfo: PTileInfo;
2669
    node: PVirtualNode;
2670
    cell: TMapCell;
2671
    ghostTile: TGhostTile;
2672
    i: Integer;
2673
  begin
2674
    tileInfo := nil;
2675
    if frmDrawSettings.rbTileList.Checked then
2676
    begin
2677
      node := vdtTiles.GetFirstSelected;
2678
      if node <> nil then
2679
        tileInfo := vdtTiles.GetNodeData(node);
2680
    end else if frmDrawSettings.rbRandom.Checked then
2681
    begin
2682
      node := vdtRandom.GetFirst;
2683
      for i := 1 to Random(vdtRandom.RootNodeCount) do
2684
        node := vdtRandom.GetNext(node);
2685
2686
      if node <> nil then
2687
        tileInfo := vdtRandom.GetNodeData(node);
2688
    end;
2689
2690
    if tileInfo <> nil then
2691
    begin
2692
      if tileInfo^.ID < $4000 then
2693
      begin
2694
        cell := FLandscape.MapCell[AX, AY];
2695
        if cell <> nil then
2696
        begin
2697
          cell.IsGhost := True;
2698
          cell.GhostID := tileInfo^.ID;
2699
          if frmDrawSettings.cbForceAltitude.Checked then
2700
            cell.GhostZ := frmDrawSettings.seForceAltitude.Value
2701
          else
2702
            cell.GhostZ := cell.RawZ;
2703
          if frmDrawSettings.cbRandomHeight.Checked then
2704
            cell.GhostZ := cell.GhostZ + Random(frmDrawSettings.seRandomHeight.Value);
2705
2706
          PrepareMapCell(cell);
2707
        end;
2708
      end else
2709
      begin
2710
        ghostTile := TGhostTile.Create(nil, nil, 0, 0);
2711
        ghostTile.TileID := tileInfo^.ID - $4000;
2712
        ghostTile.Hue := frmHueSettings.lbHue.ItemIndex;
2713
        ghostTile.X := AX;
2714
        ghostTile.Y := AY;
2715
        if not frmDrawSettings.cbForceAltitude.Checked then
2716
        begin
2717
          ghostTile.Z := ABaseTile.Z;
2718
          if ABaseTile is TStaticItem then
2719
            ghostTile.Z := ghostTile.Z +
2720
              ResMan.Tiledata.StaticTiles[ABaseTile.TileID].Height;
2721
        end else
2722
          ghostTile.Z := frmDrawSettings.seForceAltitude.Value;
2723
        if frmDrawSettings.cbRandomHeight.Checked then
2724
          ghostTile.Z := ghostTile.Z +
2725
            Random(frmDrawSettings.seRandomHeight.Value);
2726
2727
        ghostTile.UpdatePriorities(ResMan.Tiledata.StaticTiles[ghostTile.TileID],
2728
          MaxInt);
2729
        ghostTile.CanBeEdited := True;
2730
2731
        FVirtualTiles.Add(ghostTile);
2732
        blockInfo := FScreenBuffer.Insert(ghostTile);
2733
        blockInfo^.State := ssGhost;
2734
        PrepareScreenBlock(blockInfo);
2735
      end;
2736
2737
    end;
2738
  end;
2739
2740
var
2741
  selectedRect: TRect;
2742
  blockInfo: PBlockInfo;
2743
  item: TWorldItem;
2744
  cell: TMapCell;
2745
  i, tileX, tileY: Integer;
2746
begin
2747
  Logger.EnterMethod([lcClient, lcDebug], 'UpdateSelection');
2748
2749
  //If the current tile is nil, but we still have a selected tile, the
2750
  //procedure is pointless - the selection should stay intact.
2751
  if (CurrentTile <> nil) or (SelectedTile = nil) then
2752
  begin
2753
    if CurrentTile = nil then
2754
      selectedRect := Rect(-1, -1, -1, -1)
2755
    else
2756
      selectedRect := GetSelectedRect;
2757
2758
    //clean up old ghost tiles
2759
    Logger.Send([lcClient, lcDebug], 'Cleaning ghost tiles');
2760
    for i := FVirtualTiles.Count - 1 downto 0 do
2761
    begin
2762
      item := FVirtualTiles[i];
2763
      if (item is TGhostTile) and not IsInRect(item.X, item.Y, selectedRect) then
2764
      begin
2765
        FScreenBuffer.Delete(item);
2766
        FVirtualTiles.Delete(i);
2767
      end;
2768
    end;
2769
    Logger.Send([lcClient, lcDebug], 'FSelection', FSelection);
2770
    for tileX := FSelection.Left to FSelection.Right do
2771
      for tileY := FSelection.Top to FSelection.Bottom do
2772
        if not IsInRect(tileX, tileY, selectedRect) then
2773
        begin
2774
          cell := FLandscape.MapCell[tileX, tileY];
2775
          if (cell <> nil) and cell.IsGhost then
2776
          begin
2777
            cell.IsGhost := False;
2778
            PrepareMapCell(cell);
2779
          end;
2780
        end;
2781
2782
    if (CurrentTile <> nil) and (not acSelect.Checked) then
2783
    begin
2784
      blockInfo := nil;
2785
      if (SelectedTile <> nil) and (CurrentTile <> SelectedTile) then
2786
      begin
2787
        Logger.Send([lcClient, lcDebug], 'Multiple Targets');
2788
        Logger.Send([lcClient, lcDebug], 'SelectedRect', selectedRect);
2789
        //set new ghost tiles
2790
        if acDraw.Checked then
2791
          for tileX := selectedRect.Left to selectedRect.Right do
2792
            for tileY := selectedRect.Top to selectedRect.Bottom do
2793
              if not IsInRect(tileX, tileY, FSelection) then
2794
                AddGhostTile(tileX, tileY, SelectedTile);
2795
        while FScreenBuffer.Iterate(blockInfo) do
2796
          if (blockInfo^.State = ssNormal) then
2797
            SetHighlight(blockInfo, IsInRect(blockInfo^.Item.X, blockInfo^.Item.Y,
2798
              selectedRect) and not acDraw.Checked);
2799
      end else
2800
      begin
2801
        Logger.Send([lcClient, lcDebug], 'Single Target');
2802
        if acDraw.Checked and not IsInRect(CurrentTile.X, CurrentTile.Y,
2803
          FSelection) then
2804
          AddGhostTile(CurrentTile.X, CurrentTile.Y, CurrentTile);
2805
        while FScreenBuffer.Iterate(blockInfo) do
2806
          if blockInfo^.State = ssNormal then
2807
            SetHighlight(blockInfo, (blockInfo^.Item = CurrentTile) and
2808
              not acDraw.Checked);
2809
      end;
2810
    end;
2811
    FSelection := selectedRect;
2812
  end;
2813
  Logger.Send([lcClient, lcDebug], 'Virtual Tiles', FVirtualTiles.Count);
2814
  Logger.ExitMethod([lcClient, lcDebug], 'UpdateSelection');
2815
end;
2816
2817
procedure TfrmMain.OnTileRemoved(ATile: TMulBlock);
2818
begin
2819
  if ATile = FCurrentTile then
2820
    FCurrentTile := nil
2821
  else if ATile = FSelectedTile then
2822
    FSelectedTile := nil;
2823
end;
2824
2825
procedure TfrmMain.WriteChatMessage(ASender, AMessage: string);
2826
var
2827
  node: PVirtualNode;
2828
  chatInfo: PChatInfo;
2829
begin
2830
  node := vstChat.AddChild(nil);
2831
  chatInfo := vstChat.GetNodeData(node);
2832
  chatInfo^.Time := Now;
2833
  chatInfo^.Sender := ASender;
2834
  chatInfo^.Msg := AMessage;
2835
  if vstChat.RootNodeCount > 30 then
2836
    vstChat.DeleteNode(vstChat.GetFirst);
2837
  vstChat.ScrollIntoView(node, False);
2838
  
2839
  if not pnlChat.Visible then
2840
  begin
2841
    lblChatHeaderCaption.Font.Bold := True;
2842
    lblChatHeaderCaption.Font.Italic := True;
2843
    lblChatHeaderCaption.Font.Color := clRed;
2844
  end;
2845
end;
2846
2847
procedure TfrmMain.OnClientHandlingPacket(ABuffer: TEnhancedMemoryStream);
2848
var
2849
  sender, msg: string;
2850
  i: Integer;
2851
  accessLevel: TAccessLevel;
2852
begin
2853
  case ABuffer.ReadByte of
2854
    $01: //client connected
2855
      begin
2856
        sender := ABuffer.ReadStringNull;
2857
        lbClients.Items.Add(sender);
2858
        if sender <> dmNetwork.Username then
2859
          WriteChatMessage('System', Format('User "%s" has connected.', [sender]));
2860
      end;
2861
    $02:
2862
      begin
2863
        sender := ABuffer.ReadStringNull;
2864
        lbClients.Items.Delete(lbClients.Items.IndexOf(sender));
2865
        if sender <> dmNetwork.Username then
2866
          WriteChatMessage('System', Format('User "%s" has disconnected.', [sender]));
2867
      end;
2868
    $03: //Client list
2869
      begin
2870
        lbClients.Clear;
2871
        while ABuffer.Position < ABuffer.Size do
2872
          lbClients.Items.Add(ABuffer.ReadStringNull);
2873
      end;
2874
    $04: //Set pos
2875
      begin
2876
        FX := ABuffer.ReadWord;
2877
        FY := ABuffer.ReadWord;
2878
        SetPos(FX, FY);
2879
      end;
2880
    $05: //chat
2881
      begin
2882
        sender := ABuffer.ReadStringNull;
2883
        msg := ABuffer.ReadStringNull;
2884
        WriteChatMessage(sender, msg);
2885
      end;
2886
    $07: //access changed
2887
      begin
2888
        accessLevel := TAccessLevel(ABuffer.ReadByte);
2889
        FLandscape.UpdateWriteMap(ABuffer);
2890
        FRepaintNeeded := True;
2891
2892
        if accessLevel <> dmNetwork.AccessLevel then
2893
        begin
2894
          dmNetwork.AccessLevel := accessLevel;
2895
          if accessLevel = alNone then
2896
          begin
2897
            MessageDlg('AccessLevel change', 'Your account has been locked.', mtWarning, [mbOK], 0);
2898
            mnuDisconnectClick(nil);
2899
          end else
2900
          begin
2901
            ProcessAccessLevel;
2902
            MessageDlg('AccessLevel change', Format('Your accesslevel has been changed to %s.', [GetAccessLevelString(accessLevel)]), mtWarning, [mbOK], 0);
2903
          end;
2904
        end;
2905
2906
        for i := Low(FAccessChangedListeners) to High(FAccessChangedListeners) do
2907
          FAccessChangedListeners[i](accessLevel);
2908
      end;
2909
  end;
2910
end;
2911
2912
function TfrmMain.GetInternalTileID(ATile: TWorldItem): Word;
2913
begin
2914
  Result := ATile.TileID;
2915
  if ATile is TStaticItem then
2916
    Inc(Result, $4000);
2917
end;
2918
2919
function TfrmMain.GetSelectedRect: TRect;
2920
begin
2921
  if CurrentTile <> nil then
2922
  begin
2923
    if SelectedTile <> nil then
2924
    begin
2925
      Result.Left := Min(CurrentTile.X, SelectedTile.X);
2926
      Result.Top := Min(CurrentTile.Y, SelectedTile.Y);
2927
      Result.Right := Max(CurrentTile.X, SelectedTile.X);
2928
      Result.Bottom := Max(CurrentTile.Y, SelectedTile.Y);
2929
    end else
2930
    begin
2931
      Result.Left := CurrentTile.X;
2932
      Result.Top := CurrentTile.Y;
2933
      Result.Right := CurrentTile.X;
2934
      Result.Bottom := CurrentTile.Y;
2935
    end;
2936
  end;
2937
end;
2938
2939
function TfrmMain.ConfirmAction: Boolean;
2940
begin
2941
  if acMove.Checked and frmMoveSettings.cbAsk.Checked then
2942
  begin
2943
    Result := frmMoveSettings.ShowModal = mrYes;
2944
  end else
2945
  if not mnuSecurityQuestion.Checked then
2946
  begin
2947
    Result := True;
2948
  end else
2949
  begin
2950
    frmConfirmation.Left := Mouse.CursorPos.x - frmConfirmation.btnYes.Left - frmConfirmation.btnYes.Width div 2;
2951
    frmConfirmation.Top := Mouse.CursorPos.y - frmConfirmation.btnYes.Top - frmConfirmation.btnYes.Height div 2;
2952
    Result := frmConfirmation.ShowModal = mrYes;
2953
  end;
2954
2955
  if not oglGameWindow.MouseEntered then
2956
    oglGameWindowMouseLeave(nil);
2957
end;
2958
2959
function TfrmMain.FindRandomPreset(AName: String): TDOMElement;
2960
var
2961
  preset: TDOMElement;
2962
  presets: TDOMNodeList;
2963
  i: Integer;
2964
begin
2965
  presets := FRandomPresetsDoc.DocumentElement.ChildNodes;
2966
  Result := nil;
2967
  i := 0;
2968
  while (i < presets.Count) and (Result = nil) do
2969
  begin
2970
    preset := TDOMElement(presets[i]);
2971
    if SameText(preset.AttribStrings['Name'], AName) then
2972
      Result := preset
2973
    else
2974
      Inc(i);
2975
  end;
2976
end;
2977
2978
procedure TfrmMain.ForceUpdateCurrentTile;
2979
begin
2980
  CurrentTile := nil;
2981
  UpdateCurrentTile;
2982
end;
2983
2984
procedure TfrmMain.GetDrawOffset(ARelativeX, ARelativeY: Integer; out DrawX,
2985
  DrawY: Integer); inline;
2986
begin
2987
  DrawX := (oglGameWindow.Width div 2) + (ARelativeX - ARelativeY) * 22;
2988
  DrawY := (oglGamewindow.Height div 2) + (ARelativeX + ARelativeY) * 22;
2989
end;
2990
2991
initialization
2992
  {$I UfrmMain.lrs}
2993
2994
end.
2995