diff --git a/Client/ULandscape.pas b/Client/ULandscape.pas index d35d4de..3026df9 100644 --- a/Client/ULandscape.pas +++ b/Client/ULandscape.pas @@ -320,6 +320,8 @@ type Hue: Word; end; + TGhostTile = class(TStaticItem); + operator enumerator(AScreenBuffer: TScreenBuffer): TScreenBufferItemEnumerator; implementation @@ -996,6 +998,7 @@ var i, x, y: Integer; tempDrawList: TWorldItemList; staticTileData: TStaticTiledata; + blockInfo: PBlockInfo; begin ADrawList.Clear; tempDrawList := TWorldItemList.Create(False);; @@ -1042,7 +1045,11 @@ begin tempDrawList.Sort(@CompareWorldItems); for i := 0 to tempDrawList.Count - 1 do - ADrawList.Add(TWorldItem(tempDrawList[i])); + begin + blockInfo := ADrawList.Add(TWorldItem(tempDrawList[i])); + if tempDrawList[i] is TGhostTile then + blockInfo^.State := ssGhost; + end; tempDrawList.Free; end; diff --git a/Client/UUoaDesigns.pas b/Client/UUoaDesigns.pas index 0f21ce0..c1008fd 100644 --- a/Client/UUoaDesigns.pas +++ b/Client/UUoaDesigns.pas @@ -32,6 +32,19 @@ type constructor CreateFromStream(AStream: TStream); end; + TUoaDesign = class + private + FHeader: TUoaDesignHeader; + FTiles: TStaticItemList; + + constructor Create(AHeader: TUoaDesignHeader; AData: TStream); + public + property Header: TUoaDesignHeader read FHeader; + property Tiles: TStaticItemList read Ftiles; + + destructor Destroy; override; + end; + { TUoaDesigns } TUoaDesigns = class @@ -42,8 +55,7 @@ type constructor Create(AIdxFile, ABinFile: String); destructor Destroy; override; - function LoadTiles(AHeader: TUoaDesignHeader; AOffsetX, AOffsetY: Word; - AOffsetZ: ShortInt): TStaticItemList; + function LoadDesign(AHeader: TUoaDesignHeader): TUoaDesign; public property Headers: TUoaDesignHeaders read FHeaders; end; @@ -65,6 +77,49 @@ begin end; end; +{ TUoaDesign } + +constructor TUoaDesign.Create(AHeader: TUoaDesignHeader; AData: TStream); +var + i: Integer; + tile: TStaticItem; + version: Int32; + + function ReadInt: Int32; + begin + AData.Read(Result, SizeOf(Result)); + end; + +begin + FHeader := AHeader; + FTiles := TStaticItemList.Create(True); + AData.Seek(FHeader.FilePosition, soFromBeginning); + for i := 0 to FHeader.TileCount - 1 do + begin + AData.Read(version, SizeOf(version)); + if (version < 0) or (version > 1) then + raise Exception.Create('Unsupported binary version'); + + tile := TStaticItem.Create(nil); + tile.TileID := ReadInt; + tile.X := ReadInt; + tile.Y := ReadInt; + tile.Z := ReadInt; + ReadInt; // Level; unused + + if version = 1 then + tile.Hue := ReadInt; + + FTiles.Add(tile); + end; +end; + +destructor TUoaDesign.Destroy; +begin + FTiles.Free; + inherited Destroy; +end; + { TUoaDesignHeaders } constructor TUoaDesignHeaders.CreateFromStream(AStream: TStream); @@ -120,39 +175,9 @@ begin Headers.Free; end; -function TUoaDesigns.LoadTiles(AHeader: TUoaDesignHeader; AOffsetX, - AOffsetY: Word; AOffsetZ: ShortInt): TStaticItemList; -var - i: Integer; - tile: TStaticItem; - version: Int32; - - function ReadInt: Int32; - begin - FData.Read(Result, SizeOf(Result)); - end; - +function TUoaDesigns.LoadDesign(AHeader: TUoaDesignHeader): TUoaDesign; begin - Result := TStaticItemList.Create(True); - FData.Seek(AHeader.FilePosition, soFromBeginning); - for i := 0 to AHeader.TileCount - 1 do - begin - FData.Read(version, SizeOf(version)); - if (version < 0) or (version > 1) then - raise Exception.Create('Unsupported binary version'); - - tile := TStaticItem.Create(nil); - tile.TileID := ReadInt; - tile.X := AOffsetX + ReadInt; - tile.Y := AOffsetY + ReadInt; - tile.Z := AOffsetZ + ReadInt; - ReadInt; // TODO: Level?? - - if version = 1 then - tile.Hue := ReadInt; - - Result.Add(tile); - end; + Result := TUoaDesign.Create(AHeader, FData); end; end. diff --git a/Client/UfrmMain.pas b/Client/UfrmMain.pas index 719a92c..f9ce4d7 100644 --- a/Client/UfrmMain.pas +++ b/Client/UfrmMain.pas @@ -47,7 +47,6 @@ type TBlockInfoList = specialize TFPGList; - TGhostTile = class(TStaticItem); TPacketList = specialize TFPGObjectList; TAccessChangedListeners = specialize TFPGList; TSelectionListeners = specialize TFPGList; @@ -360,10 +359,13 @@ type FUndoList: TPacketList; FGLFont: TGLFont; FSelectionListeners: TSelectionListeners; + FHoverListeners: TSelectionListeners; FTileHint: TTileHintInfo; FLightManager: TLightManager; FTileFilter: TTileDataFlags; FUoaDesigns: TUoaDesigns; + FCurrentUoaDesign: TUoaDesign; + FCurrentUoaDesignAnchor: TWorldItem; { Methods } procedure BuildTileList; function ConfirmAction: Boolean; @@ -381,6 +383,7 @@ type procedure PlaceUoaDesign(AWorldItem: TWorldItem); procedure PrepareMapCell(AMapCell: TMapCell); procedure PrepareScreenBlock(ABlockInfo: PBlockInfo); + procedure PreviewUoaDesign(AWorldItem: TWorldItem); procedure ProcessToolState; procedure ProcessAccessLevel; procedure RebuildScreenBuffer; @@ -420,10 +423,12 @@ type procedure InvalidateFilter; procedure InvalidateScreenBuffer; procedure RegisterAccessChangedListener(AListener: TAccessChangedListener); + procedure RegisterHoverListener(AListener: TSelectionListener); procedure RegisterSelectionListener(AListener: TSelectionListener); procedure SetPos(AX, AY: Word); procedure SwitchToSelection; procedure UnregisterAccessChangedListener(AListener: TAccessChangedListener); + procedure UnregisterHoverListener(AListener: TSelectionListener); procedure UnregisterSelectionListener(AListener: TSelectionListener); end; @@ -1038,6 +1043,7 @@ begin pnlBottom.DoubleBuffered := True; FAccessChangedListeners := TAccessChangedListeners.Create; + FHoverListeners := TSelectionListeners.Create; FSelectionListeners := TSelectionListeners.Create; FLastDraw := Now; @@ -1408,6 +1414,7 @@ begin FreeAndNil(FGLFont); FreeAndNil(FRandomPresetsDoc); FreeAndNil(FAccessChangedListeners); + FreeAndNil(FHoverListeners); FreeAndNil(FSelectionListeners); FreeAndNil(FUoaDesigns); @@ -1989,8 +1996,24 @@ begin end; procedure TfrmMain.vstUoaDesignsDblClick(Sender: TObject); +var + selectedNode: PVirtualNode; begin + // Make sure to reset the current view first. + PreviewUoaDesign(nil); + + UnregisterSelectionListener(@PlaceUoaDesign); + UnregisterHoverListener(@PreviewUoaDesign); + + selectedNode := vstUoaDesigns.GetFirstSelected(); + if selectedNode = nil then + Exit; + + FreeAndNil(FCurrentUoaDesign); + FCurrentUoaDesign := FUoaDesigns.LoadDesign(FUoaDesigns.Headers[selectedNode^.Index]); + RegisterSelectionListener(@PlaceUoaDesign); + RegisterHoverListener(@PreviewUoaDesign); end; procedure TfrmMain.vstUoaDesignsGetText(Sender: TBaseVirtualTree; @@ -2065,6 +2088,12 @@ begin FAccessChangedListeners.Add(AListener); end; +procedure TfrmMain.RegisterHoverListener(AListener: TSelectionListener); +begin + if FHoverListeners.IndexOf(AListener) = -1 then + FHoverListeners.Add(AListener); +end; + procedure TfrmMain.RegisterSelectionListener(AListener: TSelectionListener); begin if FSelectionListeners.IndexOf(AListener) = -1 then @@ -2077,6 +2106,11 @@ begin FAccessChangedListeners.Remove(AListener); end; +procedure TfrmMain.UnregisterHoverListener(AListener: TSelectionListener); +begin + FHoverListeners.Remove(Alistener); +end; + procedure TfrmMain.UnregisterSelectionListener(AListener: TSelectionListener); begin FSelectionListeners.Remove(AListener); @@ -2294,7 +2328,9 @@ begin if selectedNode = nil then Exit; - header := FUoaDesigns.Headers[selectedNode^.Index]; + vstUoaDesigns.ClearSelection; + + {header := FUoaDesigns.Headers[selectedNode^.Index]; tiles := FUoaDesigns.LoadTiles(header, AWorldItem.X, AWorldItem.Y, AWorldItem.Z); try FUndoList.Clear; @@ -2305,7 +2341,7 @@ begin end; finally tiles.Free; - end; + end;} end; procedure TfrmMain.PrepareMapCell(AMapCell: TMapCell); @@ -2568,6 +2604,62 @@ begin end; end; +procedure TfrmMain.PreviewUoaDesign(AWorldItem: TWorldItem); +var + offsetX, offsetY, offsetZ: Integer; + newX, newY, newZ: Integer; + i: Integer; + designTile, virtualTile: TStaticItem; + blockInfo: PBlockInfo; +begin + // If nothing has changed, we can keep this short. + if FCurrentUoaDesignAnchor = AWorldItem then + Exit; + + // No design selected? Well then. + if FCurrentUoaDesign = nil then + Exit; + + for i := FVirtualTiles.Count - 1 downto 0 do + begin + if FVirtualTiles[i] is TGhostTile then + begin + FScreenBuffer.Delete(FVirtualTiles[i]); + FVirtualTiles.Delete(i); + end; + end; + + if AWorldItem = nil then + Exit; + + offsetX := AWorldItem.X - FCurrentUoaDesign.Header.Width div 2; + offsetY := AWorldItem.Y - FCurrentUoaDesign.Header.Height div 2; + offsetZ := AWorldItem.Z; + + for designTile in FCurrentUoaDesign.Tiles do + begin + newX := designTile.X + offsetX; + newY := designTile.Y + offsetY; + newZ := designTile.Z + offsetZ; + + if (newX < 0) or (newX >= FLandscape.CellWidth) or + (newY < 0) or (newY >= FLandscape.CellHeight) or + (newZ < -128) or (newZ > 127) then + // We can't render this tile. Skip it. + continue; + + virtualTile := TGhostTile.Create(nil, nil, 0, 0); + virtualTile.X := newX; + virtualTile.Y := newY; + virtualTile.Z := newZ; + virtualTile.TileID := designTile.TileID; + virtualTile.Hue := designTile.Hue; + FVirtualTiles.Add(virtualTile); + end; + + InvalidateScreenBuffer; +end; + procedure TfrmMain.Render; var highlight: Boolean; @@ -3060,6 +3152,7 @@ end; procedure TfrmMain.UpdateCurrentTile(AX, AY: Integer); var blockInfo: PBlockInfo; + listener: TSelectionListener; begin //Logger.EnterMethod([lcClient, lcDebug], 'UpdateCurrentTile'); FOverlayUI.ActiveArrow := FOverlayUI.HitTest(AX, AY); @@ -3077,6 +3170,9 @@ begin else CurrentTile := nil; + for listener in FHoverListeners do + listener(CurrentTile); + //Logger.ExitMethod([lcClient, lcDebug], 'UpdateCurrentTile'); end;