🚧 Implement actions as objects

This commit is contained in:
Andreas Schneider 2022-05-29 11:46:22 +02:00
parent dc36f0b09c
commit d75a85d269
6 changed files with 531 additions and 26 deletions

View File

@ -325,7 +325,7 @@
<MinVersion Minor="5" Release="3" Valid="True"/> <MinVersion Minor="5" Release="3" Valid="True"/>
</Item6> </Item6>
</RequiredPackages> </RequiredPackages>
<Units Count="47"> <Units Count="48">
<Unit0> <Unit0>
<Filename Value="CentrED.lpr"/> <Filename Value="CentrED.lpr"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
@ -577,6 +577,10 @@
<Filename Value="USelectionHelper.pas"/> <Filename Value="USelectionHelper.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
</Unit46> </Unit46>
<Unit47>
<Filename Value="UActions.pas"/>
<IsPartOfProject Value="True"/>
</Unit47>
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>

465
Client/UActions.pas Normal file
View File

@ -0,0 +1,465 @@
(*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* http://www.opensource.org/licenses/cddl1.php.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* http://www.opensource.org/licenses/cddl1.php. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying * information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2022 Andreas Schneider
*)
unit UActions;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, fgl, UWorldItem, UStatics, UMap, UPacket, ULandscape;
type
TPacketList = specialize TFPGObjectList<TPacket>;
TIdList = specialize TFPGList<Word>;
{ TBaseEditAction }
TBaseEditAction = class(TObject)
private
FLandscape: TLandscape;
FMapTiles: TMapCellList;
FStaticTiles: TStaticItemList;
public
constructor Create(ALandscape: TLandscape);
destructor Destroy; override;
public
property MapTiles: TMapCellList read FMapTiles;
property StaticTiles: TStaticItemList read FStaticTiles;
procedure StartSelection(ATile: TWorldItem); virtual; abstract;
procedure AddSelection(ATile: TWorldItem); virtual; abstract;
function IsHighlighted(ATile: TWorldItem): Boolean; virtual; abstract;
procedure Execute(AScreenBuffer: TScreenBuffer; out AForwardActions, AReverseActions: TPacketList); virtual;
end;
{ TRectangleEditAction }
TRectangleEditAction = class(TBaseEditAction)
private
FFirstTile: TWorldItem;
FLastTile: TWorldItem;
FRect: TRect;
public
constructor Create(ALandscape: TLandscape);
procedure StartSelection(ATile: TWorldItem); override;
procedure AddSelection(ATile: TWorldItem); override;
function IsHighlighted(ATile: TWorldItem): Boolean; override;
private
procedure UpdateArea; virtual;
function IsEditableTile(AWorldItem: TWorldItem): Boolean; inline;
function IsEditableStaticTile(AWorldItem: TWorldItem): Boolean; inline;
end;
{ TDrawAction }
TDrawAction = class(TRectangleEditAction)
private
FTileIds: TIdList;
FForceZ: Boolean;
FForceZValue: ShortInt;
FRandomZ: Boolean;
FRandomZValue: Byte;
FHue: Word;
public
property TileIds: TIdList read FTileIds;
property ForceZ: Boolean read FForceZ write FForceZ;
property ForceZValue: ShortInt read FForceZValue write FForceZValue;
property RandomZ: Boolean read FRandomZ write FRandomZ;
property RandomZValue: Byte read FRandomZValue write FRandomZValue;
constructor Create(ALandscape: TLandscape);
function IsHighlighted(ATile: TWorldItem): Boolean; override;
procedure Execute(AScreenBuffer: TScreenBuffer; out AForwardActions,
AReverseActions: TPacketList); override;
private
procedure UpdateArea; override;
end;
{ TMoveAction }
TMoveAction = class(TRectangleEditAction)
private
FOffsetX: Integer;
FOffsetY: Integer;
public
property OffsetX: Integer read FOffsetX write FOffsetX;
property OffsetY: Integer read FOffsetY write FOffsetY;
procedure Execute(AScreenBuffer: TScreenBuffer; out AForwardActions,
AReverseActions: TPacketList); override;
end;
{ TElevateAction }
TElevateAction = class(TRectangleEditAction)
public type
TMode = (emSet, emRaise, emLower);
private
FMode: TMode;
FZ: ShortInt;
FRandomZ: Boolean;
FRandomZValue: Byte;
public
property Mode: TMode read FMode write FMode;
property Z: ShortInt read FZ write FZ;
procedure Execute(AScreenBuffer: TScreenBuffer; out AForwardActions,
AReverseActions: TPacketList); override;
end;
{ TDeleteAction }
TDeleteAction = class(TRectangleEditAction)
public
procedure Execute(AScreenBuffer: TScreenBuffer; out AForwardActions,
AReverseActions: TPacketList); override;
end;
{ THueAction }
THueAction = class(TRectangleEditAction)
private
FHue: Word;
public
property Hue: Word read FHue write FHue;
procedure Execute(AScreenBuffer: TScreenBuffer; out AForwardActions,
AReverseActions: TPacketList); override;
end;
implementation
uses
Math, UGameResources, UPackets;
{ TBaseEditAction }
constructor TBaseEditAction.Create(ALandscape: TLandscape);
begin
FLandscape := ALandscape;
FMapTiles := TMapCellList.Create;
FStaticTiles := TStaticItemList.Create;
end;
destructor TBaseEditAction.Destroy;
begin
FMapTiles.Free;
FStaticTiles.Free;
inherited Destroy;
end;
procedure TBaseEditAction.Execute(AScreenBuffer: TScreenBuffer; out
AForwardActions, AReverseActions: TPacketList);
begin
AForwardActions := TPacketList.Create;
AReverseActions := TPacketList.Create;
end;
{ TRectangleEditAction }
constructor TRectangleEditAction.Create(ALandscape: TLandscape);
begin
inherited Create(ALandscape);
FRect.Left := -1;
FRect.Top := -1;
FRect.Bottom := -1;
FRect.Right := -1;
end;
procedure TRectangleEditAction.StartSelection(ATile: TWorldItem);
begin
FFirstTile := ATile;
FLastTile := ATile;
UpdateArea;
end;
procedure TRectangleEditAction.AddSelection(ATile: TWorldItem);
begin
FLastTile := ATile;
UpdateArea;
end;
function TRectangleEditAction.IsHighlighted(ATile: TWorldItem): Boolean;
begin
Result := ((FFirstTile = FLastTile) and (FFirsttile = ATile)) or
FRect.Contains(TPoint.Create(ATile.X, ATile.Y));
end;
procedure TRectangleEditAction.UpdateArea;
begin
if FFirstTile = FLastTile then
begin
FRect.Left := -1;
FRect.Top := -1;
FRect.Bottom := -1;
FRect.Right := -1;
end else
begin
FRect.Left := Min(FFirstTile.X, FLastTile.X);
FRect.Top := Min(FFirstTile.Y, FLastTile.Y);
FRect.Right := Max(FFirstTile.X, FLastTile.X);
FRect.Bottom := Max(FFirstTile.Y, FLastTile.Y);
end;
end;
function TRectangleEditAction.IsEditableTile(AWorldItem: TWorldItem): Boolean;
begin
if not AWorldItem.CanBeEdited then
Exit(False);
if FFirstTile = FLastTile then
begin
if AWorldItem <> FFirstTile then
Exit(False);
end else
begin
if not FRect.Contains(TPoint.Create(AWorldItem.X, AWorldItem.Y)) then
Exit(False);
end;
Result := True;
end;
function TRectangleEditAction.IsEditableStaticTile(AWorldItem: TWorldItem): Boolean;
begin
if not IsEditableTile(AWorldItem) then
Exit(False);
if not (AWorldItem is TStaticItem) then
Exit(False);
Result := True;
end;
{ TDrawAction }
constructor TDrawAction.Create(ALandscape: TLandscape);
begin
inherited Create(ALandscape);
FTileIds := TIdList.Create;
end;
function TDrawAction.IsHighlighted(ATile: TWorldItem): Boolean;
begin
// Since we add new tiles, we don't need to highlight any existing
// tiles.
Result := False;
end;
procedure TDrawAction.Execute(AScreenBuffer: TScreenBuffer; out
AForwardActions, AReverseActions: TPacketList);
var
staticTile: TStaticItem;
mapTile, previousMapTile: TMapCell;
begin
inherited Execute(AScreenBuffer, AForwardActions, AReverseActions);
if FTileIds.Count < 1 then
Exit;
for mapTile in FMapTiles do
begin
previousMapTile := FLandscape.MapCell[mapTile.X, mapTile.Y];
AForwardActions.Add(TDrawMapPacket.Create(mapTile.X, mapTile.Y, mapTile.Z, mapTile.TileID));
AReverseActions.Add(TDrawMapPacket.Create(previousMapTile.X, previousMapTile.Y, previousMapTile.Z, previousMapTile.TileID));
end;
for staticTile in FStaticTiles do
begin
AForwardActions.Add(TInsertStaticPacket.Create(staticTile));
AReverseActions.Add(TDeleteStaticPacket.Create(staticTile));
end;
end;
procedure TDrawAction.UpdateArea;
var
targetArea: TRect;
tileX, tileY: Word;
tileId: Word;
mapCell: TMapCell;
staticItem: TStaticItem;
worldItem: TWorldItem;
begin
inherited UpdateArea;
FMapTiles.Clear;
FStaticTiles.Clear;
// TODO Only update relevant section of the tile lists.
if FFirstTile = nil then
Exit;
targetArea.Left := Min(FFirstTile.X, FLastTile.X);
targetArea.Top := Min(FFirstTile.Y, FLastTile.Y);
targetArea.Right := Max(FFirstTile.X, FLastTile.X);
targetArea.Bottom := Max(FFirstTile.Y, FLastTile.Y);
for tileX := targetArea.Left to targetArea.Right do
for tileY := targetArea.Top to targetArea.Bottom do
begin
tileId := FTileIds[Random(FTileIds.Count)];
if tileId < $4000 then
begin
// Map Tile
mapCell := FLandscape.MapCell[tileX, tileY].Clone;
mapCell.TileID := tileId;
worldItem := mapCell;
FMapTiles.Add(mapCell);
end else
begin
// Static Tile
staticItem := TStaticItem.Create(nil, nil, 0, 0);
staticItem.TileID := tileId - $4000;
staticItem.Hue := FHue;
if not FForceZ then
begin
staticItem.Z := FFirstTile.Z;
if FFirstTile is TStaticItem then
staticItem.Z := staticItem.Z + ResMan.Tiledata.StaticTiles[FFirstTile.TileID].Height;
end;
worldItem := staticItem;
FStaticTiles.Add(staticItem);
end;
worldItem.X := tileX;
worldItem.Y := tileY;
if FForceZ then
worldItem.Z := FForceZValue;
if FRandomZ then
worldItem.Z := worldItem.Z + Random(FRandomZValue);
end;
end;
{ TDeleteAction }
procedure TDeleteAction.Execute(AScreenBuffer: TScreenBuffer; out
AForwardActions, AReverseActions: TPacketList);
var
item: TWorldItem;
begin
inherited Execute(AScreenBuffer, AForwardActions, AReverseActions);
for item in AScreenBuffer do
begin
if not IsEditableStaticTile(item) then
Continue;
AForwardActions.Add(TDeleteStaticPacket.Create(TStaticItem(item)));
AReverseActions.Add(TInsertStaticPacket.Create(TStaticItem(item)));
end;
end;
{ TMoveAction }
procedure TMoveAction.Execute(AScreenBuffer: TScreenBuffer; out
AForwardActions, AReverseActions: TPacketList);
var
newX, newY: Word;
item: TWorldItem;
begin
inherited Execute(AScreenBuffer, AForwardActions, AReverseActions);
for item in AScreenBuffer do
begin
if not IsEditableStaticTile(item) then
Continue;
newX := EnsureRange(item.X + FOffsetX, 0, FLandscape.CellWidth - 1);
newY := EnsureRange(item.Y + FOffsetY, 0, FLandscape.CellHeight - 1);
AForwardActions.Add(TMoveStaticPacket.Create(TStaticItem(item), newX, newY));
AReverseActions.Add(TMoveStaticPacket.Create(TStaticItem(item), item.X, item.Y));
end;
end;
{ TElevateAction }
procedure TElevateAction.Execute(AScreenBuffer: TScreenBuffer; out
AForwardActions, AReverseActions: TPacketList);
var
item: TWorldItem;
newZ: Integer;
begin
inherited Execute(AScreenBuffer, AForwardActions, AReverseActions);
for item in AScreenBuffer do
begin
if not IsEditableTile(item) then
Continue;
newZ := FZ;
if FRandomZ then
newZ := newZ + Random(FRandomZValue);
case FMode of
emRaise: newZ := item.Z + FZ;
emLower: newZ := item.Z - FZ;
end;
newZ := EnsureRange(newZ, -128, 127);
if item is TMapCell then
begin
AForwardActions.Add(TDrawMapPacket.Create(item.X, item.Y, newZ, item.TileID));
AReverseActions.Add(TDrawMapPacket.Create(item.X, item.Y, item.Z, item.TileID));
end else if item is TStaticItem then
begin
AForwardActions.Add(TElevateStaticPacket.Create(TStaticItem(item), newZ));
AReverseActions.Add(TElevateStaticPacket.Create(TStaticItem(item), item.Z));
end;
end;
end;
{ THueAction }
procedure THueAction.Execute(AScreenBuffer: TScreenBuffer; out AForwardActions,
AReverseActions: TPacketList);
var
item: TWorldItem;
begin
inherited Execute(AScreenBuffer, AForwardActions, AReverseActions);
for item in AScreenBuffer do
begin
if not IsEditableStaticTile(item) then
Continue;
if TStaticItem(item).Hue = FHue then
Continue;
AForwardActions.Add(THueStaticPacket.Create(TStaticItem(item), FHue));
AReverseActions.Add(THueStaticPacket.Create(TStaticItem(item), TStaticItem(item).Hue));
end;
end;
end.

View File

@ -299,6 +299,18 @@ type
{ Events } { Events }
procedure OnTileRemoved(ATile: TMulBlock); procedure OnTileRemoved(ATile: TMulBlock);
end; end;
{ TScreenBufferItemEnumerator }
TScreenBufferItemEnumerator = object
private
FScreenBuffer: TScreenBuffer;
FCurrentBlock: PBlockInfo;
FCurrentItem: TWorldItem;
public
function MoveNext: Boolean;
property Current: TWorldItem read FCurrentItem;
end;
TStaticInfo = packed record TStaticInfo = packed record
X: Word; X: Word;
@ -308,6 +320,8 @@ type
Hue: Word; Hue: Word;
end; end;
operator enumerator(AScreenBuffer: TScreenBuffer): TScreenBufferItemEnumerator;
implementation implementation
uses uses
@ -325,6 +339,21 @@ begin
GLVector[2] := AVector.data[2]; GLVector[2] := AVector.data[2];
end; end;
operator enumerator(AScreenBuffer: TScreenBuffer): TScreenBufferItemEnumerator;
begin
Result.FScreenBuffer := AScreenBuffer;
Result.MoveNext;
end;
{ TScreenBufferItemEnumerator }
function TScreenBufferItemEnumerator.MoveNext: Boolean;
begin
Result := FScreenBuffer.Iterate(FCurrentBlock);
if not Result then
FCurrentItem := nil;
end;
{ TLandTextureManager } { TLandTextureManager }
constructor TLandTextureManager.Create; constructor TLandTextureManager.Create;

View File

@ -82,8 +82,9 @@ type
{ TInsertStaticPacket } { TInsertStaticPacket }
TInsertStaticPacket = class(TPacket) TInsertStaticPacket = class(TStaticPacket)
constructor Create(AX, AY: Word; AZ: ShortInt; ATileID: Word; AHue: Word); constructor Create(AX, AY: Word; AZ: ShortInt; ATileID: Word; AHue: Word); overload;
constructor Create(AStaticItem: TStaticItem); overload;
end; end;
{ TDeleteStaticPacket } { TDeleteStaticPacket }
@ -250,6 +251,12 @@ begin
FStream.WriteWord(AHue); FStream.WriteWord(AHue);
end; end;
constructor TInsertStaticPacket.Create(AStaticItem: TStaticItem);
begin
inherited Create($07, 10);
WriteStaticItem(AStaticItem);
end;
{ TDeleteStaticPacket } { TDeleteStaticPacket }
constructor TDeleteStaticPacket.Create(AStaticItem: TStaticItem); constructor TDeleteStaticPacket.Create(AStaticItem: TStaticItem);

View File

@ -1,11 +1,11 @@
object frmMain: TfrmMain object frmMain: TfrmMain
Left = 283 Left = 87
Height = 961 Height = 961
Top = 179 Top = 70
Width = 1180 Width = 1180
ActiveControl = oglGameWindow ActiveControl = oglGameWindow
Caption = 'UO CentrED' Caption = 'UO CentrED'
ClientHeight = 928 ClientHeight = 961
ClientWidth = 1180 ClientWidth = 1180
Constraints.MinHeight = 781 Constraints.MinHeight = 781
Constraints.MinWidth = 1172 Constraints.MinWidth = 1172
@ -19,12 +19,11 @@ object frmMain: TfrmMain
Position = poScreenCenter Position = poScreenCenter
SessionProperties = 'acFlat.Checked;acNoDraw.Checked;Height;Left;mnuFlatShowHeight.Checked;mnuSecurityQuestion.Checked;mnuShowAnimations.Checked;spTileList.Top;tbStatics.Down;tbTerrain.Down;Top;Width;WindowState;mnuWhiteBackground.Checked' SessionProperties = 'acFlat.Checked;acNoDraw.Checked;Height;Left;mnuFlatShowHeight.Checked;mnuSecurityQuestion.Checked;mnuShowAnimations.Checked;spTileList.Top;tbStatics.Down;tbTerrain.Down;Top;Width;WindowState;mnuWhiteBackground.Checked'
ShowInTaskBar = stAlways ShowInTaskBar = stAlways
LCLVersion = '2.2.3.0'
WindowState = wsMaximized WindowState = wsMaximized
object pnlBottom: TPanel object pnlBottom: TPanel
Left = 0 Left = 0
Height = 49 Height = 49
Top = 879 Top = 912
Width = 1180 Width = 1180
Align = alBottom Align = alBottom
BevelOuter = bvNone BevelOuter = bvNone
@ -119,7 +118,7 @@ object frmMain: TfrmMain
end end
object pcLeft: TPageControl object pcLeft: TPageControl
Left = 0 Left = 0
Height = 841 Height = 874
Top = 38 Top = 38
Width = 350 Width = 350
ActivePage = tsTiles ActivePage = tsTiles
@ -128,7 +127,7 @@ object frmMain: TfrmMain
TabOrder = 1 TabOrder = 1
object tsTiles: TTabSheet object tsTiles: TTabSheet
Caption = 'Tiles' Caption = 'Tiles'
ClientHeight = 803 ClientHeight = 836
ClientWidth = 340 ClientWidth = 340
object lblFilter: TLabel object lblFilter: TLabel
AnchorSideLeft.Control = cbTerrain AnchorSideLeft.Control = cbTerrain
@ -153,7 +152,7 @@ object frmMain: TfrmMain
AnchorSideRight.Side = asrBottom AnchorSideRight.Side = asrBottom
AnchorSideBottom.Control = spTileList AnchorSideBottom.Control = spTileList
Left = 6 Left = 6
Height = 460 Height = 493
Hint = '-' Hint = '-'
Top = 74 Top = 74
Width = 328 Width = 328
@ -214,7 +213,7 @@ object frmMain: TfrmMain
AnchorSideBottom.Side = asrBottom AnchorSideBottom.Side = asrBottom
Left = 0 Left = 0
Height = 261 Height = 261
Top = 542 Top = 575
Width = 340 Width = 340
Anchors = [akTop, akLeft, akRight, akBottom] Anchors = [akTop, akLeft, akRight, akBottom]
Caption = 'Random pool' Caption = 'Random pool'
@ -553,7 +552,7 @@ object frmMain: TfrmMain
Cursor = crVSplit Cursor = crVSplit
Left = 0 Left = 0
Height = 8 Height = 8
Top = 534 Top = 567
Width = 340 Width = 340
Align = alNone Align = alNone
Anchors = [akLeft, akRight, akBottom] Anchors = [akLeft, akRight, akBottom]
@ -567,7 +566,7 @@ object frmMain: TfrmMain
Left = 172 Left = 172
Height = 38 Height = 38
Hint = 'Append S or T to restrict the search to Statics or Terrain.' Hint = 'Append S or T to restrict the search to Statics or Terrain.'
Top = 484 Top = 517
Width = 150 Width = 150
Anchors = [akRight, akBottom] Anchors = [akRight, akBottom]
BorderSpacing.Right = 12 BorderSpacing.Right = 12
@ -628,11 +627,11 @@ object frmMain: TfrmMain
end end
object tsClients: TTabSheet object tsClients: TTabSheet
Caption = 'Clients' Caption = 'Clients'
ClientHeight = 803 ClientHeight = 836
ClientWidth = 340 ClientWidth = 340
object lbClients: TListBox object lbClients: TListBox
Left = 0 Left = 0
Height = 803 Height = 836
Top = 0 Top = 0
Width = 340 Width = 340
Align = alClient Align = alClient
@ -647,7 +646,7 @@ object frmMain: TfrmMain
end end
object tsLocations: TTabSheet object tsLocations: TTabSheet
Caption = 'Locations' Caption = 'Locations'
ClientHeight = 803 ClientHeight = 836
ClientWidth = 340 ClientWidth = 340
object btnClearLocations: TSpeedButton object btnClearLocations: TSpeedButton
AnchorSideLeft.Control = btnDeleteLocation AnchorSideLeft.Control = btnDeleteLocation
@ -656,7 +655,7 @@ object frmMain: TfrmMain
Left = 194 Left = 194
Height = 35 Height = 35
Hint = 'Clear' Hint = 'Clear'
Top = 762 Top = 795
Width = 36 Width = 36
BorderSpacing.Left = 6 BorderSpacing.Left = 6
Glyph.Data = { Glyph.Data = {
@ -707,7 +706,7 @@ object frmMain: TfrmMain
Left = 152 Left = 152
Height = 35 Height = 35
Hint = 'Delete' Hint = 'Delete'
Top = 762 Top = 795
Width = 36 Width = 36
Anchors = [akLeft, akBottom] Anchors = [akLeft, akBottom]
BorderSpacing.Bottom = 6 BorderSpacing.Bottom = 6
@ -757,7 +756,7 @@ object frmMain: TfrmMain
Left = 110 Left = 110
Height = 35 Height = 35
Hint = 'Add' Hint = 'Add'
Top = 762 Top = 795
Width = 36 Width = 36
Anchors = [akTop, akRight] Anchors = [akTop, akRight]
BorderSpacing.Right = 6 BorderSpacing.Right = 6
@ -809,7 +808,7 @@ object frmMain: TfrmMain
AnchorSideBottom.Control = btnDeleteLocation AnchorSideBottom.Control = btnDeleteLocation
Cursor = 63 Cursor = 63
Left = 6 Left = 6
Height = 750 Height = 783
Top = 6 Top = 6
Width = 328 Width = 328
Anchors = [akTop, akLeft, akRight, akBottom] Anchors = [akTop, akLeft, akRight, akBottom]
@ -1039,7 +1038,7 @@ object frmMain: TfrmMain
AnchorSideBottom.Control = spChat AnchorSideBottom.Control = spChat
Left = 350 Left = 350
Height = 35 Height = 35
Top = 634 Top = 667
Width = 830 Width = 830
Anchors = [akLeft, akRight, akBottom] Anchors = [akLeft, akRight, akBottom]
BevelInner = bvRaised BevelInner = bvRaised
@ -1075,7 +1074,7 @@ object frmMain: TfrmMain
AnchorSideBottom.Control = pnlBottom AnchorSideBottom.Control = pnlBottom
Left = 350 Left = 350
Height = 202 Height = 202
Top = 677 Top = 710
Width = 830 Width = 830
Anchors = [akTop, akLeft, akRight, akBottom] Anchors = [akTop, akLeft, akRight, akBottom]
BevelOuter = bvNone BevelOuter = bvNone
@ -1141,7 +1140,7 @@ object frmMain: TfrmMain
Cursor = crVSplit Cursor = crVSplit
Left = 350 Left = 350
Height = 8 Height = 8
Top = 669 Top = 702
Width = 830 Width = 830
Align = alNone Align = alNone
Anchors = [akLeft, akRight, akBottom] Anchors = [akLeft, akRight, akBottom]
@ -1158,7 +1157,7 @@ object frmMain: TfrmMain
AnchorSideRight.Side = asrBottom AnchorSideRight.Side = asrBottom
AnchorSideBottom.Control = pnlChatHeader AnchorSideBottom.Control = pnlChatHeader
Left = 350 Left = 350
Height = 596 Height = 629
Top = 38 Top = 38
Width = 830 Width = 830
Anchors = [akTop, akLeft, akRight, akBottom] Anchors = [akTop, akLeft, akRight, akBottom]

View File

@ -36,7 +36,8 @@ uses
StdCtrls, Spin, UEnums, laz.VirtualTrees, Buttons, UMulBlock, UWorldItem, math, StdCtrls, Spin, UEnums, laz.VirtualTrees, Buttons, UMulBlock, UWorldItem, math,
LCLIntf, UOverlayUI, UStatics, UEnhancedMemoryStream, ActnList, LCLIntf, UOverlayUI, UStatics, UEnhancedMemoryStream, ActnList,
XMLPropStorage, ImagingClasses, dateutils, UPlatformTypes, UMap, UPacket, XMLPropStorage, ImagingClasses, dateutils, UPlatformTypes, UMap, UPacket,
UGLFont, DOM, XMLRead, XMLWrite, strutils, ULightManager, fgl, UTiledata; UGLFont, DOM, XMLRead, XMLWrite, strutils, ULightManager, fgl, UTiledata,
UActions;
type type
TAccessChangedListener = procedure(AAccessLevel: TAccessLevel) of object; TAccessChangedListener = procedure(AAccessLevel: TAccessLevel) of object;