466 lines
12 KiB
Plaintext
466 lines
12 KiB
Plaintext
|
(*
|
||
|
* 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.
|
||
|
|