Statistics
| Branch: | Tag: | Revision:

root / Client / ULandscape.pas @ 157:0b95089e72d4

History | View | Annotate | Download (47.5 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 ULandscape;
27
28
{$mode objfpc}{$H+}
29
30
interface
31
32
uses
33
  SysUtils, Classes, math, matrix, LCLIntf, GL, GLu, ImagingOpenGL, Imaging,
34
  ImagingClasses, ImagingTypes, ImagingUtility,
35
  UGenericIndex, UMap, UStatics, UArt, UTexture, UTiledata, UHue, UWorldItem,
36
  UMulBlock, UAnimData,
37
  UEnhancedMemoryStream, UGLFont,
38
  UCacheManager;
39
40
type
41
  TGlVector3f = array[0..2] of GLfloat;
42
  PNormals = ^TNormals;
43
  TNormals = array[0..3] of TGlVector3f;
44
  PRadarBlock = ^TRadarBlock;
45
  TRadarBlock = array[0..7, 0..7] of Word;
46
  
47
  { TMaterial }
48
  
49
  TMaterial = class(ICacheable)
50
    constructor Create;
51
    destructor Destroy; override;
52
  protected
53
    FRefCount: Integer;
54
    FWidth: Integer;
55
    FHeight: Integer;
56
    FRealWidth: Integer;
57
    FRealHeight: Integer;
58
    FGraphic: TMultiImage;
59
    class procedure CalculateTextureDimensions(ACaps: TGLTextureCaps; ARealWidth,
60
      ARealHeight: Integer; out AWidth, AHeight: Integer);
61
    function GenerateTexture(AImage: TBaseImage): TGLuint;
62
    function GetTexture: GLuint; virtual; abstract;
63
  public
64
    property Width: Integer read FWidth;
65
    property Height: Integer read FHeight;
66
    property RealWidth: Integer read FRealWidth;
67
    property RealHeight: Integer read FRealHeight;
68
    property Texture: GLuint read GetTexture;
69
70
    procedure AddRef;
71
    procedure DelRef;
72
    function HitTest(AX, AY: Integer): Boolean;
73
74
    {ICacheable}
75
    function CanBeRemoved: Boolean;
76
    procedure RemoveFromCache;
77
  end;
78
79
  { TSimpleMaterial }
80
81
  TSimpleMaterial = class(TMaterial)
82
    constructor Create(AGraphic: TBaseImage);
83
    destructor Destroy; override;
84
  protected
85
    FTexture: TGLuint;
86
    function GetTexture: GLuint; override;
87
  end;
88
89
  { TAnimMaterial }
90
91
  TAnimMaterial = class(TMaterial)
92
    constructor Create(ABaseID: Word; AAnimData: TAnimData; AHue: THue = nil;
93
      APartialHue: Boolean = False);
94
    destructor Destroy; override;
95
  protected
96
    FActiveFrame: Byte;
97
    FNextChange: DWord;
98
    FAnimData: TAnimData;
99
    FTextures: array of TGLuint;
100
    function GetTexture: GLuint; override;
101
  end;
102
103
  TMaterialCache = specialize TCacheManager<TMaterial>;
104
  
105
  { TLandTextureManager }
106
  
107
  TLandTextureManager = class
108
    constructor Create;
109
    destructor Destroy; override;
110
  protected
111
    FArtCache: TMaterialCache;
112
    FTexCache: TMaterialCache;
113
    FAnimCache: TMaterialCache;
114
    FUseAnims: Boolean;
115
  public
116
    property UseAnims: Boolean read FUseAnims write FUseAnims;
117
    function GetArtMaterial(ATileID: Word): TMaterial; overload;
118
    function GetArtMaterial(ATileID: Word; AHue: THue;
119
      APartialHue: Boolean): TMaterial; overload;
120
    function GetStaticMaterial(AStaticItem: TStaticItem;
121
      AOverrideHue: Integer = -1): TMaterial;
122
    function GetTexMaterial(ATileID: Word): TMaterial;
123
  end;
124
125
 { TSeperatedStaticBlock }
126
127
  TSeperatedStaticBlock = class(TStaticBlock)
128
    constructor Create(AData: TStream; AIndex: TGenericIndex; AX, AY: Word); overload;
129
    constructor Create(AData: TStream; AIndex: TGenericIndex); overload;
130
    destructor Destroy; override;
131
  public
132
    Cells: array[0..63] of TStaticItemList;
133
    { Methods }
134
    function Clone: TSeperatedStaticBlock; override;
135
    function GetSize: Integer; override;
136
    procedure RebuildList;
137
  end;
138
139
  TLandscape = class;
140
  
141
  { TBlock }
142
143
  TBlock = class
144
    constructor Create(AMap: TMapBlock; AStatics: TStaticBlock);
145
    destructor Destroy; override;
146
  protected
147
    { Fields }
148
    FMapBlock: TMapBlock;
149
    FStaticBlock: TStaticBlock;
150
  public
151
    { Fields }
152
    property Map: TMapBlock read FMapBlock;
153
    property Static: TStaticBlock read FStaticBlock;
154
    { Methods }
155
    procedure UpdateBlockAcess(ALandscape: TLandscape);
156
  end;
157
  
158
  TLandscapeChangeEvent = procedure of object;
159
  TMapChangedEvent = procedure(AMapCell: TMapCell) of object;
160
  TNewBlockEvent = procedure(ABlock: TBlock) of object;
161
  TStaticChangedEvent = procedure(AStaticItem: TStaticItem) of object;
162
163
  TScreenBuffer = class;
164
  TBlockCache = specialize TCacheManager<TBlock>;
165
166
  { TLandscape }
167
168
  TLandscape = class
169
    constructor Create(AWidth, AHeight: Word);
170
    destructor Destroy; override;
171
  protected
172
    { Members }
173
    FWidth: Word;
174
    FHeight: Word;
175
    FCellWidth: Word;
176
    FCellHeight: Word;
177
    FBlockCache: TBlockCache;
178
    FOnChange: TLandscapeChangeEvent;
179
    FOnMapChanged: TMapChangedEvent;
180
    FOnNewBlock: TNewBlockEvent;
181
    FOnStaticInserted: TStaticChangedEvent;
182
    FOnStaticDeleted: TStaticChangedEvent;
183
    FOnStaticElevated: TStaticChangedEvent;
184
    FOnStaticHued: TStaticChangedEvent;
185
    FOpenRequests: TBits;
186
    FWriteMap: TBits;
187
    FDrawMap: TBits;
188
    FMaxStaticID: Cardinal;
189
    { Methods }
190
    function GetMapBlock(AX, AY: Word): TMapBlock;
191
    function GetMapCell(AX, AY: Word): TMapCell;
192
    function GetNormals(AX, AY: Word): TNormals;
193
    function GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
194
    function GetStaticList(AX, AY: Word): TStaticItemList;
195
    { Events }
196
    procedure OnRemoveCachedObject(ABlock: TBlock);
197
    procedure OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
198
    procedure OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
199
    procedure OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
200
    procedure OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
201
    procedure OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
202
    procedure OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
203
    procedure OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
204
  public
205
    { Fields }
206
    property Width: Word read FWidth;
207
    property Height: Word read FHeight;
208
    property CellWidth: Word read FCellWidth;
209
    property CellHeight: Word read FCellHeight;
210
    property MapCell[X, Y: Word]: TMapCell read GetMapCell;
211
    property StaticList[X, Y: Word]: TStaticItemList read GetStaticList;
212
    property Normals[X, Y: Word]: TNormals read GetNormals;
213
    property MaxStaticID: Cardinal read FMaxStaticID;
214
    property OnChange: TLandscapeChangeEvent read FOnChange write FOnChange;
215
    property OnMapChanged: TMapChangedEvent read FOnMapChanged write FOnMapChanged;
216
    property OnNewBlock: TNewBlockEvent read FOnNewBlock write FOnNewBlock;
217
    property OnStaticInserted: TStaticChangedEvent read FOnStaticInserted
218
      write FOnStaticInserted;
219
    property OnStaticDeleted: TStaticChangedEvent read FOnStaticDeleted
220
      write FOnStaticDeleted;
221
    property OnStaticElevated: TStaticChangedEvent read FOnStaticElevated
222
      write FOnStaticElevated;
223
    property OnStaticHued: TStaticChangedEvent read FOnStaticHued
224
      write FOnStaticHued;
225
    { Methods }
226
    function CanWrite(AX, AY: Word): Boolean;
227
    procedure FillDrawList(ADrawList: TScreenBuffer; AX, AY, AWidth,
228
      AHeight: Word; AMap, AStatics: Boolean; ANoDraw: Boolean;
229
      AAdditionalTiles: TWorldItemList = nil);
230
    function GetEffectiveAltitude(ATile: TMapCell): ShortInt;
231
    function GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
232
    procedure GetNormals(AX, AY: Word; var ANormals: TNormals);
233
    procedure LoadNoDrawMap(AFileName: String);
234
    procedure MoveStatic(AStatic: TStaticItem; AX, AY: Word);
235
    procedure PrepareBlocks(AX1, AY1, AX2, AY2: Word);
236
    procedure UpdateBlockAccess;
237
    procedure UpdateWriteMap(AStream: TEnhancedMemoryStream);
238
  end;
239
240
  { TGLText }
241
242
  TGLText = class
243
    constructor Create(AFont: TGLFont; AText: String);
244
  protected
245
    FFont: TGLFont;
246
    FText: String;
247
    FWidth: Integer;
248
    FHeight: Integer;
249
  public
250
    procedure Render(AScreenRect: TRect);
251
  end;
252
253
  TScreenState = (ssNormal, ssFiltered, ssGhost);
254
  TWalkRestriction = (wrNone, wrCanWalk, wrCannotWalk);
255
256
  PBlockInfo = ^TBlockInfo;
257
  TBlockInfo = record
258
    ScreenRect: TRect;
259
    DrawQuad: array[0..3,0..1] of TGLint;
260
    RealQuad: array[0..3,0..1] of TGLint;
261
    Item: TWorldItem;
262
    HighRes: TMaterial;
263
    LowRes: TMaterial;
264
    Normals: PNormals;
265
    State: TScreenState;
266
    Highlighted: Boolean;
267
    HueOverride: Boolean;
268
    CheckRealQuad: Boolean;
269
    Translucent: Boolean;
270
    WalkRestriction: TWalkRestriction;
271
    Text: TGLText;
272
    Next: PBlockInfo;
273
  end;
274
275
  { TScreenBuffer }
276
277
  TScreenBuffer = class
278
    constructor Create; virtual;
279
    destructor Destroy; override;
280
  protected
281
    { Members }
282
    FCount: Cardinal;
283
    FShortCuts: array[-1..10] of PBlockInfo; //-1 = last, 0 = first, 1..10 = other shortcuts
284
    FShortCutsValid: Boolean;
285
    FSerial: Cardinal;
286
  public
287
    { Methods }
288
    function Add(AItem: TWorldItem): PBlockInfo;
289
    procedure Clear;
290
    procedure Delete(AItem: TWorldItem);
291
    function Find(AScreenPosition: TPoint): PBlockInfo;
292
    function GetSerial: Cardinal;
293
    function Insert(AItem: TWorldItem): PBlockInfo;
294
    function Iterate(var ABlockInfo: PBlockInfo): Boolean;
295
    procedure UpdateShortcuts;
296
    function UpdateSortOrder(AItem: TWorldItem): PBlockInfo;
297
    { Events }
298
    procedure OnTileRemoved(ATile: TMulBlock);
299
  end;
300
  
301
  TStaticInfo = packed record
302
    X: Word;
303
    Y: Word;
304
    Z: ShortInt;
305
    TileID: Word;
306
    Hue: Word;
307
  end;
308
309
implementation
310
311
uses
312
  UGameResources, UdmNetwork, UPackets, UPacketHandlers, Logging;
313
314
function GetID(AX, AY: Word): Integer; inline;
315
begin
316
  Result := (AX shl 16) or AY;
317
end;
318
319
operator := (AVector: Tvector3_single) GLVector: TGlVector3f;
320
begin
321
  GLVector[0] := AVector.data[0];
322
  GLVector[1] := AVector.data[1];
323
  GLVector[2] := AVector.data[2];
324
end;
325
326
{ TLandTextureManager }
327
328
constructor TLandTextureManager.Create;
329
begin
330
  inherited Create;
331
  FArtCache := TMaterialCache.Create(1024);
332
  FTexCache := TMaterialCache.Create(128);
333
  FAnimCache := TMaterialCache.Create(128);
334
  FUseAnims := True;
335
end;
336
337
destructor TLandTextureManager.Destroy;
338
begin
339
  FreeAndNil(FArtCache);
340
  FreeAndNil(FTexCache);
341
  FreeAndNil(FAnimCache);
342
  inherited Destroy;
343
end;
344
345
function TLandTextureManager.GetArtMaterial(ATileID: Word): TMaterial;
346
var
347
  artEntry: TArt;
348
  animData: TAnimData;
349
begin
350
  Result := nil;
351
352
  if FUseAnims and (ATileID >= $4000) and (tdfAnimation in
353
      ResMan.Tiledata.StaticTiles[ATileID - $4000].Flags) then
354
  begin
355
    animData := ResMan.Animdata.AnimData[ATileID - $4000];
356
    if (animData.FrameCount > 0) and not FAnimCache.QueryID(ATileID, Result) then
357
    begin
358
      Result := TAnimMaterial.Create(ATileID, animData);
359
      FAnimCache.StoreID(ATileID, Result);
360
    end;
361
  end;
362
363
  if (Result = nil) and not FArtCache.QueryID(ATileID, Result) then
364
  begin
365
    artEntry := TArt(ResMan.Art.Block[ATileID]);
366
367
    Result := TSimpleMaterial.Create(artEntry.Graphic);
368
    FArtCache.StoreID(ATileID, Result);
369
370
    artEntry.Free;
371
  end;
372
373
  Result.AddRef;
374
end;
375
376
function TLandTextureManager.GetArtMaterial(ATileID: Word; AHue: THue;
377
  APartialHue: Boolean): TMaterial;
378
var
379
  artEntry: TArt;
380
  animData: TAnimData;
381
  id: Integer;
382
begin
383
  if AHue = nil then
384
  begin
385
    Result := GetArtMaterial(ATileID);
386
  end else
387
  begin
388
    Result := nil;
389
    id := ATileID or (((AHue.ID + 1) and $3FFF) shl 16) or (Byte(APartialHue) shl 30);
390
391
    if FUseAnims and (ATileID >= $4000) and (tdfAnimation in
392
      ResMan.Tiledata.StaticTiles[ATileID - $4000].Flags) then
393
    begin
394
      animData := ResMan.Animdata.AnimData[ATileID - $4000];
395
      if (animData.FrameCount > 0) and not FAnimCache.QueryID(id, Result) then
396
      begin
397
        Result := TAnimMaterial.Create(ATileID, animData, AHue, APartialHue);
398
        FAnimCache.StoreID(id, Result);
399
      end;
400
    end;
401
402
    if (Result = nil) and not FArtCache.QueryID(id, Result) then
403
    begin
404
      artEntry := ResMan.Art.GetArt(ATileID, 0, AHue, APartialHue);
405
406
      Result := TSimpleMaterial.Create(artEntry.Graphic);
407
      FArtCache.StoreID(id, Result);
408
409
      artEntry.Free;
410
    end;
411
    Result.AddRef;
412
  end;
413
end;
414
415
function TLandTextureManager.GetStaticMaterial(AStaticItem: TStaticItem;
416
  AOverrideHue: Integer = -1): TMaterial;
417
var
418
  staticTiledata: TStaticTiledata;
419
  hue: THue;
420
begin
421
  staticTiledata := ResMan.Tiledata.StaticTiles[AStaticItem.TileID];
422
  if AOverrideHue < 0 then
423
    AOverrideHue := AStaticItem.Hue;
424
425
  if AOverrideHue > 0 then
426
    hue := ResMan.Hue.Hues[AOverrideHue - 1]
427
  else
428
    hue := nil;
429
430
  Result := GetArtMaterial($4000 + AStaticItem.TileID, hue,
431
    tdfPartialHue in staticTiledata.Flags);
432
end;
433
434
function TLandTextureManager.GetTexMaterial(ATileID: Word): TMaterial;
435
var
436
  texEntry: TTexture;
437
  texID: Integer;
438
begin
439
  if not FTexCache.QueryID(ATileID, Result) then
440
  begin
441
    texID := ResMan.Tiledata.LandTiles[ATileID].TextureID;
442
    if texID > 0 then
443
    begin
444
      texEntry := TTexture(ResMan.Texmaps.Block[texID]);
445
446
      Result := TSimpleMaterial.Create(texEntry.Graphic);
447
      FTexCache.StoreID(ATileID, Result);
448
449
      texEntry.Free;
450
    end else
451
      Result := nil;
452
  end;
453
454
  if Result <> nil then
455
    Result.AddRef;
456
end;
457
458
{ TSeperatedStaticBlock }
459
460
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex;
461
  AX, AY: Word);
462
var
463
  i: Integer;
464
  item: TStaticItem;
465
  block: TMemoryStream;
466
begin
467
  inherited Create;
468
  FItems := TStaticItemList.Create(False);
469
470
  FX := AX;
471
  FY := AY;
472
473
  for i := 0 to 63 do
474
    Cells[i] := TStaticItemList.Create;
475
476
  if (AData <> nil) and (AIndex.Lookup > 0) and (AIndex.Size > 0) then
477
  begin
478
    AData.Position := AIndex.Lookup;
479
    block := TMemoryStream.Create;
480
    block.CopyFrom(AData, AIndex.Size);
481
    block.Position := 0;
482
    for i := 1 to (AIndex.Size div 7) do
483
    begin
484
      item := TStaticItem.Create(Self, block, AX, AY);
485
      Cells[(item.Y mod 8) * 8 + (item.X mod 8)].Add(item);
486
    end;
487
    block.Free;
488
  end;
489
end;
490
491
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex);
492
begin
493
  Create(AData, AIndex, 0, 0);
494
end;
495
496
destructor TSeperatedStaticBlock.Destroy;
497
var
498
  i: Integer;
499
begin
500
  FreeAndNil(FItems);
501
502
  for i := 0 to 63 do
503
  begin
504
    if Cells[i] <> nil then
505
      FreeAndNil(Cells[i]);
506
  end;
507
508
  inherited Destroy;
509
end;
510
511
function TSeperatedStaticBlock.Clone: TSeperatedStaticBlock;
512
begin
513
  raise Exception.Create('TSeperatedStaticBlock.Clone is not implemented (yet).');
514
  Result := nil;
515
end;
516
517
function TSeperatedStaticBlock.GetSize: Integer;
518
begin
519
  RebuildList;
520
  Result := inherited GetSize;
521
end;
522
523
procedure TSeperatedStaticBlock.RebuildList;
524
var
525
  i, j, solver: Integer;
526
begin
527
  FItems.Clear;
528
  solver := 0;
529
  for i := 0 to 63 do
530
  begin
531
    if Cells[i] <> nil then
532
    begin
533
      for j := 0 to Cells[i].Count - 1 do
534
      begin
535
        FItems.Add(Cells[i].Items[j]);
536
        TStaticItem(Cells[i].Items[j]).UpdatePriorities(
537
          ResMan.Tiledata.StaticTiles[TStaticItem(Cells[i].Items[j]).TileID],
538
          solver);
539
        Inc(solver);
540
      end;
541
    end;
542
  end;
543
  Sort;
544
end;
545
546
{ TBlock }
547
548
constructor TBlock.Create(AMap: TMapBlock; AStatics: TStaticBlock);
549
begin
550
  inherited Create;
551
  FMapBlock := AMap;
552
  FStaticBlock := AStatics;
553
end;
554
555
destructor TBlock.Destroy;
556
begin
557
  if FMapBlock <> nil then FreeAndNil(FMapBlock);
558
  if FStaticBlock <> nil then FreeAndNil(FStaticBlock);
559
  inherited Destroy;
560
end;
561
562
procedure TBlock.UpdateBlockAcess(ALandscape: TLandscape);
563
var
564
  staticItem: TStaticItem;
565
  i: Integer;
566
begin
567
  for i := Low(FMapBlock.Cells) to High(FMapBlock.Cells) do
568
  begin
569
    FMapBlock.Cells[i].CanBeEdited := ALandscape.CanWrite(FMapBlock.Cells[i].X,
570
      FMapBlock.Cells[i].Y);
571
  end;
572
573
  if FStaticBlock is TSeperatedStaticBlock then
574
    TSeperatedStaticBlock(FStaticBlock).RebuildList; //fill items
575
576
  for i := 0 to FStaticBlock.Items.Count - 1 do
577
  begin
578
    staticItem := FStaticBlock.Items[i];
579
    staticItem.CanBeEdited := ALandscape.CanWrite(staticItem.X,
580
      staticItem.Y);
581
  end;
582
end;
583
584
{ TLandscape }
585
586
constructor TLandscape.Create(AWidth, AHeight: Word);
587
var
588
  i: Integer;
589
begin
590
  inherited Create;
591
  FWidth := AWidth;
592
  FHeight := AHeight;
593
  FCellWidth := FWidth * 8;
594
  FCellHeight := FHeight * 8;
595
  FBlockCache := TBlockCache.Create(256);
596
  FBlockCache.OnRemoveObject := @OnRemoveCachedObject;
597
598
  FOnChange := nil;
599
  FOnNewBlock := nil;
600
  FOnStaticDeleted := nil;
601
  FOnStaticElevated := nil;
602
  FOnStaticHued := nil;
603
  FOnStaticInserted := nil;
604
605
  FOpenRequests := TBits.Create(FWidth * FHeight);
606
  FOpenRequests.Clearall; //set all to 0
607
  FWriteMap := TBits.Create(FCellWidth * FCellHeight);
608
  for i := 0 to FWriteMap.Size - 1 do
609
    FWriteMap[i] := True;
610
611
  FMaxStaticID := Min(Min(ResMan.Animdata.AnimCount, ResMan.Tiledata.StaticCount),
612
    ResMan.Art.EntryCount - $4000);
613
  Logger.Send([lcClient, lcInfo], 'Landscape recognizes $%x StaticTile IDs.',
614
    [FMaxStaticId]);
615
616
  FDrawMap := TBits.Create($4000 + FMaxStaticID);
617
  for i := 0 to FDrawMap.Size - 1 do
618
    FDrawMap[i] := True;
619
620
  RegisterPacketHandler($04, TPacketHandler.Create(0, @OnBlocksPacket));
621
  RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket));
622
  RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket));
623
  RegisterPacketHandler($08, TPacketHandler.Create(10, @OnDeleteStaticPacket));
624
  RegisterPacketHandler($09, TPacketHandler.Create(11, @OnElevateStaticPacket));
625
  RegisterPacketHandler($0A, TPacketHandler.Create(14, @OnMoveStaticPacket));
626
  RegisterPacketHandler($0B, TPacketHandler.Create(12, @OnHueStaticPacket));
627
end;
628
629
destructor TLandscape.Destroy;
630
begin
631
  if FBlockCache <> nil then
632
  begin
633
    FBlockCache.OnRemoveObject := nil;
634
    FreeAndNil(FBlockCache);
635
  end;
636
637
  FreeAndNil(FOpenRequests);
638
  FreeAndNil(FWriteMap);
639
  FreeAndNil(FDrawMap);
640
  
641
  RegisterPacketHandler($04, nil);
642
  RegisterPacketHandler($06, nil);
643
  RegisterPacketHandler($07, nil);
644
  RegisterPacketHandler($08, nil);
645
  RegisterPacketHandler($09, nil);
646
  RegisterPacketHandler($0A, nil);
647
  RegisterPacketHandler($0B, nil);
648
  
649
  inherited Destroy;
650
end;
651
652
function TLandscape.GetMapBlock(AX, AY: Word): TMapBlock;
653
var
654
  block: TBlock;
655
begin
656
  Result := nil;
657
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
658
  begin
659
    if FBlockCache.QueryID(GetID(AX, AY), block) then
660
      Result := block.Map;
661
  end;
662
end;
663
664
function TLandscape.GetMapCell(AX, AY: Word): TMapCell;
665
var
666
  block: TMapBlock;
667
begin
668
  Result := nil;
669
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
670
  begin
671
    block := GetMapBlock(AX div 8, AY div 8);
672
    if block <> nil then
673
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
674
  end;
675
end;
676
677
function TLandscape.GetNormals(AX, AY: Word): TNormals;
678
begin
679
  GetNormals(AX, AY, Result);
680
end;
681
682
function TLandscape.GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
683
var
684
  block: TBlock;
685
begin
686
  Result := nil;
687
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
688
  begin
689
    if FBlockCache.QueryID(GetID(AX, AY), block) then
690
      Result := TSeperatedStaticBlock(block.Static);
691
  end;
692
end;
693
694
function TLandscape.GetStaticList(AX, AY: Word): TStaticItemList;
695
var
696
  block: TSeperatedStaticBlock;
697
begin
698
  Result := nil;
699
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
700
  begin
701
    block := GetStaticBlock(AX div 8, AY div 8);
702
    if block <> nil then
703
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
704
  end;
705
end;
706
707
procedure TLandscape.OnRemoveCachedObject(ABlock: TBlock);
708
begin
709
  if ABlock <> nil then
710
    dmNetwork.Send(TFreeBlockPacket.Create(ABlock.Map.X, ABlock.Map.Y));
711
end;
712
713
procedure TLandscape.OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
714
var
715
  index: TGenericIndex;
716
  map: TMapBlock;
717
  statics: TStaticBlock;
718
  coords: TBlockCoords;
719
  count: Word;
720
  id: Integer;
721
  block: TBlock;
722
begin
723
  index := TGenericIndex.Create(nil);
724
  while ABuffer.Position < ABuffer.Size do
725
  begin
726
    ABuffer.Read(coords, SizeOf(TBlockCoords));
727
    id := GetID(coords.X, coords.Y);
728
729
    map := TMapBlock.Create(ABuffer, coords.X, coords.Y);
730
    count := ABuffer.ReadWord;
731
    if count > 0 then
732
      index.Lookup := ABuffer.Position
733
    else
734
      index.Lookup := -1;
735
    index.Size := count * 7;
736
    statics := TSeperatedStaticBlock.Create(ABuffer, index, coords.X, coords.Y);
737
738
    FBlockCache.RemoveID(id);
739
    block := TBlock.Create(map, statics);
740
    block.UpdateBlockAcess(Self);
741
    FBlockCache.StoreID(id, block);
742
743
    FOpenRequests[coords.Y * FWidth + coords.X] := False;
744
745
    if Assigned(FOnNewBlock) then FOnNewBlock(block);
746
  end;
747
  index.Free;
748
end;
749
750
procedure TLandscape.OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
751
var
752
  x, y: Word;
753
  cell: TMapCell;
754
begin
755
  x := ABuffer.ReadWord;
756
  y := ABuffer.ReadWord;
757
  cell := GetMapCell(x, y);
758
  if cell <> nil then
759
  begin
760
    cell.Altitude := ABuffer.ReadShortInt;
761
    cell.TileID := ABuffer.ReadWord;
762
    if Assigned(FOnMapChanged) then FOnMapChanged(cell);
763
  end;
764
end;
765
766
procedure TLandscape.OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
767
var
768
  x, y: Word;
769
  block: TSeperatedStaticBlock;
770
  staticItem: TStaticItem;
771
  targetStaticList: TStaticItemList;
772
  i: Integer;
773
begin
774
  x := ABuffer.ReadWord;
775
  y := ABuffer.ReadWord;
776
  block := GetStaticBlock(x div 8, y div 8);
777
  if block <> nil then
778
  begin
779
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
780
    staticItem.X := x;
781
    staticItem.Y := y;
782
    staticItem.Z := ABuffer.ReadShortInt;
783
    staticItem.TileID := ABuffer.ReadWord;
784
    staticItem.Hue := ABuffer.ReadWord;
785
    targetStaticList := block.Cells[(y mod 8) * 8 + x mod 8];
786
    targetStaticList.Add(staticItem);
787
    for i := 0 to targetStaticList.Count - 1 do
788
      targetStaticList.Items[i].UpdatePriorities(
789
        ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
790
        i);
791
    targetStaticList.Sort(@CompareStaticItems);
792
    staticItem.Owner := block;
793
    staticItem.CanBeEdited := CanWrite(x, y);
794
795
    if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
796
  end;
797
end;
798
799
procedure TLandscape.OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
800
var
801
  block: TSeperatedStaticBlock;
802
  i: Integer;
803
  statics: TStaticItemList;
804
  staticInfo: TStaticInfo;
805
  staticItem: TStaticItem;
806
begin
807
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
808
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
809
  if block <> nil then
810
  begin
811
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
812
    for i := 0 to statics.Count - 1 do
813
    begin
814
      staticItem := statics.Items[i];
815
      if (staticItem.Z = staticInfo.Z) and
816
         (staticItem.TileID = staticInfo.TileID) and
817
         (staticItem.Hue = staticInfo.Hue) then
818
      begin
819
        if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
820
        staticItem.Delete;
821
        statics.Delete(i);
822
823
        Break;
824
      end;
825
    end;
826
  end;
827
end;
828
829
procedure TLandscape.OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
830
var
831
  block: TSeperatedStaticBlock;
832
  i,j : Integer;
833
  statics: TStaticItemList;
834
  staticInfo: TStaticInfo;
835
  staticItem: TStaticItem;
836
begin
837
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
838
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
839
  if block <> nil then
840
  begin
841
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
842
    for i := 0 to statics.Count - 1 do
843
    begin
844
      staticItem := statics.Items[i];
845
      if (staticItem.Z = staticInfo.Z) and
846
         (staticItem.TileID = staticInfo.TileID) and
847
         (staticItem.Hue = staticInfo.Hue) then
848
      begin
849
        staticItem.Z := ABuffer.ReadShortInt;
850
        for j := 0 to statics.Count - 1 do
851
          statics.Items[j].UpdatePriorities(
852
            ResMan.Tiledata.StaticTiles[statics.Items[j].TileID],
853
            j);
854
        statics.Sort(@CompareStaticItems);
855
856
        if Assigned(FOnStaticElevated) then FOnStaticElevated(staticItem);
857
858
        Break;
859
      end;
860
    end;
861
  end;
862
end;
863
864
procedure TLandscape.OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
865
var
866
  sourceBlock, targetBlock: TSeperatedStaticBlock;
867
  i: Integer;
868
  statics: TStaticItemList;
869
  staticInfo: TStaticInfo;
870
  staticItem: TStaticItem;
871
  newX, newY: Word;
872
begin
873
  staticItem := nil;
874
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
875
  newX := EnsureRange(ABuffer.ReadWord, 0, FCellWidth - 1);
876
  newY := EnsureRange(ABuffer.ReadWord, 0, FCellHeight - 1);
877
878
  sourceBlock := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
879
  targetBlock := GetStaticBlock(newX div 8, newY div 8);
880
  if sourceBlock <> nil then
881
  begin
882
    statics := sourceBlock.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
883
    i := 0;
884
    while (i < statics.Count) and (staticItem = nil) do
885
    begin
886
      staticItem := statics.Items[i];
887
      if (staticItem.Z <> staticInfo.Z) or
888
         (staticItem.TileID <> staticInfo.TileID) or
889
         (staticItem.Hue <> staticInfo.Hue) then
890
      begin
891
        staticItem := nil;
892
      end;
893
      Inc(i);
894
    end;
895
896
    if staticItem <> nil then
897
    begin
898
      if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
899
      staticItem.Delete;
900
      statics.Remove(staticItem);
901
    end;
902
  end;
903
904
  if targetBlock <> nil then
905
  begin
906
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
907
    staticItem.X := newX;
908
    staticItem.Y := newY;
909
    staticItem.Z := staticInfo.Z;
910
    staticItem.TileID := staticInfo.TileID;
911
    staticItem.Hue := staticInfo.Hue;
912
    statics := targetBlock.Cells[(newY mod 8) * 8 + newX mod 8];
913
    statics.Add(staticItem);
914
    for i := 0 to statics.Count - 1 do
915
      TStaticItem(statics.Items[i]).UpdatePriorities(
916
        ResMan.Tiledata.StaticTiles[TStaticItem(statics.Items[i]).TileID],
917
        i);
918
    statics.Sort(@CompareStaticItems);
919
    staticItem.Owner := targetBlock;
920
    staticItem.CanBeEdited := CanWrite(newX, newY);
921
922
    if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
923
  end;
924
end;
925
926
procedure TLandscape.OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
927
var
928
  block: TSeperatedStaticBlock;
929
  i : Integer;
930
  statics: TStaticItemList;
931
  staticInfo: TStaticInfo;
932
  staticItem: TStaticItem;
933
begin
934
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
935
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
936
  if block <> nil then
937
  begin
938
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
939
    for i := 0 to statics.Count - 1 do
940
    begin
941
      staticItem := statics.Items[i];
942
      if (staticItem.Z = staticInfo.Z) and
943
         (staticItem.TileID = staticInfo.TileID) and
944
         (staticItem.Hue = staticInfo.Hue) then
945
      begin
946
        staticItem.Hue := ABuffer.ReadWord;
947
        if Assigned(FOnStaticHued) then FOnStaticHued(staticItem);
948
        Break;
949
      end;
950
    end;
951
  end;
952
end;
953
954
function TLandscape.CanWrite(AX, AY: Word): Boolean;
955
begin
956
  Result := FWriteMap[AX * FCellHeight + AY];
957
end;
958
959
procedure TLandscape.FillDrawList(ADrawList: TScreenBuffer; AX, AY, AWidth,
960
  AHeight: Word; AMap, AStatics: Boolean; ANoDraw: Boolean;
961
  AAdditionalTiles: TWorldItemList = nil);
962
var
963
  drawMapCell: TMapCell;
964
  drawStatics: TStaticItemList;
965
  i, x, y: Integer;
966
  tempDrawList: TWorldItemList;
967
  staticTileData: TStaticTiledata;
968
begin
969
  ADrawList.Clear;
970
  tempDrawList := TWorldItemList.Create(False);;
971
  for x := AX to AX + AWidth do
972
  begin
973
    for y := AY to AY + AWidth do
974
    begin
975
      if AMap then
976
      begin
977
        drawMapCell := GetMapCell(x, y);
978
        if (drawMapCell <> nil) and (ANoDraw or FDrawMap[drawMapCell.TileID]) then
979
        begin
980
          drawMapCell.Priority := GetEffectiveAltitude(drawMapCell);
981
          drawMapCell.PriorityBonus := 0;
982
          drawMapCell.PrioritySolver := 0;
983
          tempDrawList.Add(drawMapCell);
984
        end;
985
      end;
986
987
      if AStatics then
988
      begin
989
        drawStatics := GetStaticList(x, y);
990
        if drawStatics <> nil then
991
          for i := 0 to drawStatics.Count - 1 do
992
          begin
993
            staticTileData := ResMan.Tiledata.StaticTiles[drawStatics[i].TileID];
994
            if ANoDraw or FDrawMap[drawStatics[i].TileID + $4000] then
995
            begin
996
              drawStatics[i].UpdatePriorities(staticTileData,
997
                ADrawList.GetSerial);
998
              tempDrawList.Add(drawStatics[i]);
999
            end;
1000
          end;
1001
      end;
1002
    end;
1003
  end;
1004
1005
  for i := 0 to AAdditionalTiles.Count - 1 do
1006
    tempDrawList.Add(AAdditionalTiles[i]);
1007
1008
  tempDrawList.Sort(@CompareWorldItems);
1009
  for i := 0 to tempDrawList.Count - 1 do
1010
    ADrawList.Add(TWorldItem(tempDrawList[i]));
1011
  tempDrawList.Free;
1012
end;
1013
1014
function TLandscape.GetEffectiveAltitude(ATile: TMapCell): ShortInt;
1015
var
1016
  north, west, south, east: ShortInt;
1017
begin
1018
  north := ATile.Altitude;
1019
  west := GetLandAlt(ATile.X, ATile.Y + 1, north);
1020
  south := GetLandAlt(ATile.X + 1, ATile.Y + 1, north);
1021
  east := GetLandAlt(ATile.X + 1, ATile.Y, north);
1022
1023
  if Abs(north - south) >= Abs(west - east) then
1024
    Result := Min(north, south) + Abs(west - east) div 2
1025
  else
1026
    Result := Min(north, south) + Abs(north - south) div 2;
1027
end;
1028
1029
function TLandscape.GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
1030
var
1031
  cell: TMapCell;
1032
begin
1033
  cell := MapCell[AX, AY];
1034
  if cell <> nil then
1035
    Result := cell.Altitude
1036
  else
1037
    Result := ADefault;
1038
end;
1039
1040
procedure TLandscape.GetNormals(AX, AY: Word; var ANormals: TNormals);
1041
type
1042
  _Normals = array[0..3] of Tvector3_single;
1043
var
1044
  cells: array[0..2, 0..2] of _Normals;
1045
  north, west, south, east: Tvector3_single;
1046
  i, j: Integer;
1047
1048
  function Normalize(const AVector: Tvector3_single): Tvector3_single; inline;
1049
  begin
1050
    Result := AVector / AVector.length;
1051
  end;
1052
1053
  function GetPlainNormals(X, Y: SmallInt): _Normals;
1054
  var
1055
    cell: TMapCell;
1056
    north, west, south, east: ShortInt;
1057
    u, v: Tvector3_single;
1058
  begin
1059
    cell := GetMapCell(X, Y);
1060
    if cell <> nil then
1061
    begin
1062
      north := cell.Altitude;
1063
      west := GetLandAlt(cell.X, cell.Y + 1, north);
1064
      south := GetLandAlt(cell.X + 1, cell.Y + 1, north);
1065
      east := GetLandAlt(cell.X + 1, cell.Y, north);
1066
    end else
1067
    begin
1068
      north := 0;
1069
      west := 0;
1070
      east := 0;
1071
      south := 0;
1072
    end;
1073
1074
    if (north = west) and (west = east) and (north = south) then
1075
    begin
1076
      Result[0].init(0, 0, 1);
1077
      Result[1].init(0, 0, 1);
1078
      Result[2].init(0, 0, 1);
1079
      Result[3].init(0, 0, 1);
1080
    end else
1081
    begin
1082
      u.init(-22, 22, (north - east) * 4);
1083
      v.init(-22, -22, (west - north) * 4);
1084
      Result[0] := Normalize(u >< v);
1085
1086
      u.init(22, 22, (east - south) * 4);
1087
      v.init(-22, 22, (north - east) * 4);
1088
      Result[1] := Normalize(u >< v);
1089
1090
      u.init(22, -22, (south - west) * 4);
1091
      v.init(22, 22, (east - south) * 4);
1092
      Result[2] := Normalize(u >< v);
1093
1094
      u.init(-22, -22, (west - north) * 4);
1095
      v.init(22, -22, (south - west) * 4);
1096
      Result[3] := Normalize(u >< v);
1097
    end;
1098
  end;
1099
begin
1100
  for i := 0 to 2 do
1101
    for j := 0 to 2 do
1102
      cells[i, j] := GetPlainNormals(AX - 1 + i, AY - 1 + j);
1103
1104
  north := cells[0, 0][2];
1105
  west := cells[0, 1][1];
1106
  east := cells[1, 0][3];
1107
  south := cells[1, 1][0];
1108
  ANormals[0] := Normalize(north + west + east + south);
1109
1110
  north := cells[1, 0][2];
1111
  west := cells[1, 1][1];
1112
  east := cells[2, 0][3];
1113
  south := cells[2, 1][0];
1114
  ANormals[1] := Normalize(north + west + east + south);
1115
1116
  north := cells[1, 1][2];
1117
  west := cells[1, 2][1];
1118
  east := cells[2, 1][3];
1119
  south := cells[2, 2][0];
1120
  ANormals[2] := Normalize(north + west + east + south);
1121
1122
  north := cells[0, 1][2];
1123
  west := cells[0, 2][1];
1124
  east := cells[1, 1][3];
1125
  south := cells[1, 2][0];
1126
  ANormals[3] := Normalize(north + west + east + south);
1127
end;
1128
1129
procedure TLandscape.LoadNoDrawMap(AFileName: String);
1130
var
1131
  noDrawFile: TextFile;
1132
  line, ids1, ids2: String;
1133
  i, id1, id2, splitPos: Integer;
1134
begin
1135
  AssignFile(noDrawFile, AFileName);
1136
  Reset(noDrawFile);
1137
  while not EOF(noDrawFile) do
1138
  begin
1139
    ReadLn(noDrawFile, line);
1140
    if (Length(line) > 0) and (line[1] in ['S', 'T']) then
1141
    begin
1142
      splitPos := Pos('-', line);
1143
      if splitPos > 1 then
1144
      begin
1145
        ids1 := Copy(line, 2, splitPos - 2);
1146
        ids2 := Copy(line, splitPos + 1, Length(line));
1147
        if TryStrToInt(ids1, id1) and TryStrToInt(ids2, id2) then
1148
        begin
1149
          if line[1] = 'S' then
1150
          begin
1151
            Inc(id1, $4000);
1152
            Inc(id2, $4000);
1153
          end;
1154
1155
          for i := id1 to id2 do
1156
            if i < FDrawMap.Size then
1157
              FDrawMap[i] := False;
1158
        end;
1159
      end else
1160
      begin
1161
        ids1 := Copy(line, 2, Length(line));
1162
        if TryStrToInt(ids1, id1) then
1163
        begin
1164
          if line[1] = 'S' then
1165
            Inc(id1, $4000);
1166
          if id1 < FDrawMap.Size then
1167
            FDrawMap[id1] := False;
1168
        end;
1169
      end;
1170
    end;
1171
  end;
1172
  CloseFile(noDrawFile);
1173
end;
1174
1175
procedure TLandscape.MoveStatic(AStatic: TStaticItem; AX, AY: Word);
1176
var
1177
  sourceBlock, targetBlock: TSeperatedStaticBlock;
1178
  targetStaticList: TStaticItemList;
1179
  i: Integer;
1180
begin
1181
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
1182
  begin
1183
    sourceBlock := AStatic.Owner as TSeperatedStaticBlock;
1184
    targetBlock := GetStaticBlock(AX div 8, AY div 8);
1185
    if (sourceBlock <> nil) and (targetBlock <> nil) then
1186
    begin
1187
      sourceBlock.Cells[(AStatic.Y mod 8) * 8 + AStatic.X mod 8].Remove(AStatic);
1188
      targetStaticList := targetBlock.Cells[(AY mod 8) * 8 + AX mod 8];
1189
      targetStaticList.Add(AStatic);
1190
      for i := 0 to targetStaticList.Count - 1 do
1191
        targetStaticList.Items[i].UpdatePriorities(
1192
          ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
1193
          i);
1194
      targetStaticList.Sort(@CompareStaticItems);
1195
      AStatic.UpdatePos(AX, AY, AStatic.Z);
1196
      AStatic.Owner := targetBlock;
1197
    end;
1198
  end;
1199
end;
1200
1201
procedure TLandscape.PrepareBlocks(AX1, AY1, AX2, AY2: Word);
1202
var
1203
  x, y, i: Integer;
1204
  coords: TBlockCoordsArray;
1205
  block: TBlock;
1206
begin
1207
  AX1 := EnsureRange(AX1, 0, FWidth - 1);
1208
  AY1 := EnsureRange(AY1, 0, FHeight - 1);
1209
  AX2 := EnsureRange(AX2, 0, FWidth - 1);
1210
  AY2 := EnsureRange(AY2, 0, FHeight - 1);
1211
1212
  SetLength(coords, 0);
1213
  for x := AX1 to AX2 do
1214
  begin
1215
    for y := AY1 to AY2 do
1216
    begin
1217
      if (not FOpenRequests[y * FWidth + x]) and
1218
         (not FBlockCache.QueryID(GetID(x, y), block)) then
1219
      begin
1220
        SetLength(coords, Length(coords) + 1);
1221
        i := High(coords);
1222
        coords[i].X := x;
1223
        coords[i].Y := y;
1224
        FOpenRequests[y * FWidth + x] := True;
1225
      end;
1226
    end;
1227
  end;
1228
  if Length(coords) > 0 then
1229
    dmNetwork.Send(TRequestBlocksPacket.Create(coords));
1230
end;
1231
1232
procedure TLandscape.UpdateBlockAccess;
1233
var
1234
  cacheEntry: TBlockCache.PCacheEntry;
1235
begin
1236
  cacheEntry := nil;
1237
  while FBlockCache.Iterate(cacheEntry) do
1238
    if cacheEntry^.Obj <> nil then
1239
      cacheEntry^.Obj.UpdateBlockAcess(Self);
1240
end;
1241
1242
procedure TLandscape.UpdateWriteMap(AStream: TEnhancedMemoryStream);
1243
var
1244
  x1, y1, x2, y2: Word;
1245
  i, areaCount, cellX, cellY: Integer;
1246
begin
1247
  Logger.EnterMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
1248
1249
  areaCount := AStream.ReadWord;
1250
  Logger.Send([lcLandscape, lcDebug], 'AreaCount', areaCount);
1251
1252
  if areaCount > 0 then
1253
  begin
1254
    FWriteMap.Clearall;
1255
    for i := 0 to areaCount - 1 do
1256
    begin
1257
      x1 := AStream.ReadWord;
1258
      y1 := AStream.ReadWord;
1259
      x2 := AStream.ReadWord;
1260
      y2 := AStream.ReadWord;
1261
      for cellX := x1 to x2 do
1262
        for cellY := y1 to y2 do
1263
          FWriteMap[cellX * FCellHeight + cellY] := True;
1264
    end;
1265
  end else
1266
    for i := 0 to FWriteMap.Size - 1 do
1267
      FWriteMap[i] := True;
1268
1269
  Logger.Send([lcLandscape, lcDebug], 'WriteMap @ 0,0', FWriteMap[0]);
1270
1271
  UpdateBlockAccess;
1272
  Logger.ExitMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
1273
end;
1274
1275
{ TMaterial }
1276
1277
constructor TMaterial.Create;
1278
begin
1279
  FRefCount := 1;
1280
end;
1281
1282
destructor TMaterial.Destroy;
1283
begin
1284
  FreeAndNil(FGraphic);
1285
  inherited Destroy;
1286
end;
1287
1288
class procedure TMaterial.CalculateTextureDimensions(ACaps: TGLTextureCaps;
1289
  ARealWidth, ARealHeight: Integer; out AWidth, AHeight: Integer);
1290
begin
1291
  if ACaps.NonPowerOfTwo then
1292
  begin
1293
    AWidth := ARealWidth;
1294
    AHeight := ARealHeight;
1295
  end else
1296
  begin
1297
    if IsPow2(ARealWidth) then
1298
      AWidth := ARealWidth
1299
    else
1300
      AWidth := NextPow2(ARealWidth);
1301
1302
    if IsPow2(ARealHeight) then
1303
      AHeight := ARealHeight
1304
    else
1305
      AHeight := NextPow2(ARealHeight);
1306
  end;
1307
end;
1308
1309
function TMaterial.GenerateTexture(AImage: TBaseImage): TGLuint;
1310
begin
1311
  Result := CreateGLTextureFromImage(AImage.ImageDataPointer^);
1312
  glBindTexture(GL_TEXTURE_2D, Result);
1313
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1314
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1315
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1316
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1317
end;
1318
1319
procedure TMaterial.AddRef;
1320
begin
1321
  Inc(FRefCount);
1322
end;
1323
1324
procedure TMaterial.DelRef;
1325
begin
1326
  Dec(FRefCount);
1327
  if FRefCount < 1 then
1328
    Free;
1329
end;
1330
1331
function TMaterial.HitTest(AX, AY: Integer): Boolean;
1332
var
1333
  pixel: TColor32Rec;
1334
begin
1335
  Result := False;
1336
  if InRange(AX, 0, FGraphic.Width - 1) and
1337
     InRange(AY, 0, FGraphic.Height - 1) then
1338
  begin
1339
    pixel := GetPixel32(FGraphic.ImageDataPointer^, AX, AY);
1340
    if pixel.A > 0 then
1341
      Result := True;
1342
  end;
1343
end;
1344
1345
function TMaterial.CanBeRemoved: Boolean;
1346
begin
1347
  Result := FRefCount <= 1;
1348
end;
1349
1350
procedure TMaterial.RemoveFromCache;
1351
begin
1352
  DelRef;
1353
end;
1354
1355
{ TScreenBuffer }
1356
1357
constructor TScreenBuffer.Create;
1358
begin
1359
  inherited Create;
1360
  FCount := 0;
1361
  FSerial := 0;
1362
  UpdateShortcuts;
1363
end;
1364
1365
destructor TScreenBuffer.Destroy;
1366
begin
1367
  Clear;
1368
  inherited Destroy;
1369
end;
1370
1371
function TScreenBuffer.Add(AItem: TWorldItem): PBlockInfo;
1372
begin
1373
  New(Result);
1374
  AItem.Locked := True;
1375
  AItem.OnDestroy.RegisterEvent(@OnTileRemoved);
1376
  Result^.Item := AItem;
1377
  Result^.HighRes := nil;
1378
  Result^.LowRes := nil;
1379
  Result^.Normals := nil;
1380
  Result^.State := ssNormal;
1381
  Result^.Highlighted := False;
1382
  Result^.Translucent := False;
1383
  Result^.Text := nil;
1384
  Result^.Next := nil;
1385
1386
  if FShortCuts[0] = nil then //First element
1387
  begin
1388
    FShortCuts[0] := Result;
1389
    FShortCuts[-1] := Result; //Last element
1390
  end else
1391
  begin
1392
    FShortCuts[-1]^.Next := Result;
1393
    FShortCuts[-1] := Result;
1394
  end;
1395
1396
  Inc(FCount);
1397
end;
1398
1399
procedure TScreenBuffer.Clear;
1400
var
1401
  current, next: PBlockInfo;
1402
begin
1403
  current := FShortCuts[0];
1404
  while current <> nil do
1405
  begin
1406
    next := current^.Next;
1407
    current^.Item.Locked := False;
1408
    current^.Item.OnDestroy.UnregisterEvent(@OnTileRemoved);
1409
    if current^.Normals <> nil then Dispose(current^.Normals);
1410
    if current^.HighRes <> nil then current^.HighRes.DelRef;
1411
    if current^.LowRes <> nil then current^.LowRes.DelRef;
1412
    current^.Text.Free;
1413
    Dispose(current);
1414
    current := next;
1415
  end;
1416
  FShortCuts[0] := nil;
1417
  FShortCuts[-1] := nil;
1418
1419
  FCount := 0;
1420
  FSerial := 0;
1421
1422
  UpdateShortcuts;
1423
end;
1424
1425
procedure TScreenBuffer.Delete(AItem: TWorldItem);
1426
var
1427
  current, last, next: PBlockInfo;
1428
begin
1429
  last := nil;
1430
  current := FShortCuts[0];
1431
  while current <> nil do
1432
  begin
1433
    if current^.Item = AItem then
1434
    begin
1435
      if FShortCuts[-1] = current then FShortCuts[-1] := last;
1436
      if FShortCuts[0] = current then FShortCuts[0] := current^.Next;
1437
      if last <> nil then last^.Next := current^.Next;
1438
1439
      if current^.Normals <> nil then Dispose(current^.Normals);
1440
      if current^.HighRes <> nil then current^.HighRes.DelRef;
1441
      if current^.LowRes <> nil then current^.LowRes.DelRef;
1442
      current^.Text.Free;
1443
1444
      Dispose(current);
1445
      Dec(FCount);
1446
      FShortCutsValid := False;
1447
      next := nil;
1448
    end else
1449
      next := current^.Next;
1450
1451
    last := current;
1452
    current := next;
1453
  end;
1454
end;
1455
1456
function TScreenBuffer.Find(AScreenPosition: TPoint): PBlockInfo;
1457
var
1458
  current: PBlockInfo;
1459
  buff: array[0..3] of GLuint;
1460
begin
1461
  Result := nil;
1462
  current := FShortCuts[0];
1463
  while current <> nil do //search the last matching tile
1464
  begin
1465
    if (current^.State = ssNormal) and
1466
       PtInRect(current^.ScreenRect, AScreenPosition)then
1467
    begin
1468
      if current^.CheckRealQuad then
1469
      begin
1470
        //OpenGL hit test
1471
        //We use the "real quad" here to prevent the draw-preview from
1472
        //intercepting with our actual tiles (which are "hidden" then).
1473
        glSelectBuffer(4, @buff[0]);
1474
        glViewport(current^.ScreenRect.Left, current^.ScreenRect.Top,
1475
          current^.ScreenRect.Right, current^.ScreenRect.Bottom);
1476
        glRenderMode(GL_SELECT);
1477
        glInitNames;
1478
        glPushName(0);
1479
1480
        glPushMatrix;
1481
          glMatrixMode(GL_PROJECTION);
1482
          glLoadIdentity;
1483
          gluOrtho2D(AScreenPosition.x, AScreenPosition.x + 1,
1484
            AScreenPosition.y + 1, AScreenPosition.y);
1485
          glMatrixMode(GL_MODELVIEW);
1486
          glLoadIdentity;
1487
1488
          glBegin(GL_QUADS);
1489
            glVertex2iv(@current^.RealQuad[0]);
1490
            glVertex2iv(@current^.RealQuad[3]);
1491
            glVertex2iv(@current^.RealQuad[2]);
1492
            glVertex2iv(@current^.RealQuad[1]);
1493
          glEnd;
1494
        glPopMatrix;
1495
        glFlush;
1496
1497
        if glRenderMode(GL_RENDER) > 0 then //glRenderMode now returns the number of hits
1498
          Result := current;
1499
      end else
1500
      if current^.LowRes.HitTest(AScreenPosition.x - current^.ScreenRect.Left,
1501
         AScreenPosition.y - current^.ScreenRect.Top) then
1502
        Result := current;
1503
    end;
1504
    current := current^.Next;
1505
  end;
1506
end;
1507
1508
function TScreenBuffer.GetSerial: Cardinal;
1509
begin
1510
  Result := FSerial;
1511
  Inc(FSerial);
1512
end;
1513
1514
function TScreenBuffer.Insert(AItem: TWorldItem): PBlockInfo;
1515
var
1516
  current: PBlockInfo;
1517
  shortcut: Integer;
1518
begin
1519
  if not FShortCutsValid then
1520
    UpdateShortcuts;
1521
1522
  New(Result);
1523
  AItem.Locked := True;
1524
  AItem.OnDestroy.RegisterEvent(@OnTileRemoved);
1525
  Result^.Item := AItem;
1526
  Result^.HighRes := nil;
1527
  Result^.LowRes := nil;
1528
  Result^.Normals := nil;
1529
  Result^.State := ssNormal;
1530
  Result^.Highlighted := False;
1531
  Result^.Translucent := False;
1532
  Result^.Text := nil;
1533
1534
  if (FShortCuts[0] = nil) or (CompareWorldItems(AItem, FShortCuts[0]^.Item) < 0) then
1535
  begin
1536
    if FShortCuts[0] = nil then
1537
      FShortCuts[-1] := Result;  //Update last item
1538
1539
    Result^.Next := FShortCuts[0];
1540
    FShortCuts[0] := Result;
1541
  end else
1542
  begin
1543
    //find best entry point
1544
    shortcut := 0;
1545
    while (shortcut <= 10) and (FShortCuts[shortcut] <> nil) and
1546
      (CompareWorldItems(AItem, FShortCuts[shortcut]^.Item) >= 0) do
1547
    begin
1548
      current := FShortCuts[shortcut];
1549
      Inc(shortcut);
1550
    end;
1551
1552
    //now find the real match
1553
    while (current^.Next <> nil) and
1554
          (CompareWorldItems(AItem, current^.Next^.Item) > 0) do
1555
    begin
1556
      current := current^.Next;
1557
    end;
1558
1559
    if FShortCuts[-1] = current^.Next then
1560
      FShortCuts[-1] := Result;  //Update last item
1561
1562
    Result^.Next := current^.Next;
1563
    current^.Next := Result;
1564
  end;
1565
1566
  Inc(FCount);
1567
end;
1568
1569
function TScreenBuffer.Iterate(var ABlockInfo: PBlockInfo): Boolean;
1570
begin
1571
  if ABlockInfo = nil then
1572
    ABlockInfo := FShortCuts[0]
1573
  else
1574
    ABlockInfo := ABlockInfo^.Next;
1575
  Result := ABlockInfo <> nil;
1576
end;
1577
1578
procedure TScreenBuffer.UpdateShortcuts;
1579
var
1580
  shortcut, step, nextStep, stepSize: Integer;
1581
  blockInfo: PBlockInfo;
1582
begin
1583
  if FCount < 10 then
1584
  begin
1585
    for shortcut := 1 to 10 do
1586
      FShortCuts[shortcut] := nil;
1587
  end
1588
  else if FShortCuts[0] <> nil then
1589
  begin
1590
    stepSize := FCount div 10;
1591
    nextStep := stepSize;
1592
    step := 0;
1593
    shortcut := 1;
1594
    blockInfo := FShortCuts[0];
1595
    repeat
1596
      if step = nextStep then
1597
      begin
1598
        FShortCuts[shortcut] := blockInfo;
1599
        Inc(shortcut);
1600
        Inc(nextStep, stepSize);
1601
      end;
1602
1603
      Inc(step);
1604
1605
      FShortCuts[-1] := blockInfo; //update last known item
1606
      blockInfo := blockInfo^.Next;
1607
    until (blockInfo = nil);
1608
  end;
1609
  FShortCutsValid := True;
1610
end;
1611
1612
function TScreenBuffer.UpdateSortOrder(AItem: TWorldItem): PBlockInfo;
1613
var
1614
  newNodePosition, oldNode, oldNodePrev, current: PBlockInfo;
1615
begin
1616
  newNodePosition := nil;
1617
  oldNode := nil;
1618
  oldNodePrev := nil;
1619
  current := FShortCuts[0];
1620
1621
  while (current <> nil) and ((oldNode = nil) or (newNodePosition = nil)) do
1622
  begin
1623
    if current^.Item = AItem then
1624
      oldNode := current
1625
    else if oldNode = nil then
1626
      oldNodePrev := current;
1627
1628
    if newNodePosition = nil then
1629
    begin
1630
      if (current^.Next = nil) or (CompareWorldItems(AItem, current^.Next^.Item) < 0) then
1631
        newNodePosition := current;
1632
    end;
1633
1634
    current := current^.Next;
1635
  end;
1636
1637
  //oldNode = nil, if the change happend out-of-screen
1638
  if (oldNode <> nil ) and (oldNode <> newNodePosition) then
1639
  begin
1640
    if oldNodePrev <> oldNode then
1641
    begin
1642
      if oldNodePrev = nil then
1643
        FShortCuts[0] := oldNode^.Next
1644
      else
1645
        oldNodePrev^.Next := oldNode^.Next;
1646
    end;
1647
1648
    if (newNodePosition = FShortCuts[0]) and (CompareWorldItems(AItem, FShortCuts[0]^.Item) < 0) then
1649
    begin
1650
      oldNode^.Next := FShortCuts[0];
1651
      FShortCuts[0] := oldNode;
1652
    end else
1653
    begin
1654
      oldNode^.Next := newNodePosition^.Next;
1655
      newNodePosition^.Next := oldNode;
1656
    end;
1657
  end;
1658
1659
  Result := oldNode;
1660
end;
1661
1662
procedure TScreenBuffer.OnTileRemoved(ATile: TMulBlock);
1663
begin
1664
  Delete(TWorldItem(ATile));
1665
end;
1666
1667
{ TGLText }
1668
1669
constructor TGLText.Create(AFont: TGLFont; AText: String);
1670
begin
1671
  FFont := AFont;
1672
  FText := AText;
1673
  FWidth := FFont.GetTextWidth(AText);
1674
  FHeight := FFont.GetTextHeight('A');
1675
end;
1676
1677
procedure TGLText.Render(AScreenRect: TRect);
1678
var
1679
  x, y: Integer;
1680
begin
1681
  y := AScreenRect.Top + (AScreenRect.Bottom - AScreenRect.Top - FHeight) div 2;
1682
  x := AScreenRect.Left + (AScreenRect.Right - AScreenRect.Left - FWidth) div 2;
1683
  FFont.DrawText(x, y, FText);
1684
end;
1685
1686
{ TSimpleMaterial }
1687
1688
constructor TSimpleMaterial.Create(AGraphic: TBaseImage);
1689
var
1690
  caps: TGLTextureCaps;
1691
begin
1692
  inherited Create;
1693
  FRealWidth := AGraphic.Width;
1694
  FRealHeight := AGraphic.Height;
1695
1696
  GetGLTextureCaps(caps);
1697
  CalculateTextureDimensions(caps, FRealWidth, FRealHeight, FWidth, FHeight);
1698
  FGraphic := TMultiImage.CreateFromParams(FWidth, FHeight, ifA8R8G8B8, 1);
1699
  AGraphic.CopyTo(0, 0, FRealWidth, FRealHeight, FGraphic, 0, 0);
1700
  FTexture := GenerateTexture(FGraphic);
1701
end;
1702
1703
destructor TSimpleMaterial.Destroy;
1704
begin
1705
  if FTexture <> 0 then glDeleteTextures(1, @FTexture);
1706
  inherited Destroy;
1707
end;
1708
1709
function TSimpleMaterial.GetTexture: GLuint;
1710
begin
1711
  Result := FTexture;
1712
end;
1713
1714
{ TAnimMaterial }
1715
1716
constructor TAnimMaterial.Create(ABaseID: Word; AAnimData: TAnimData;
1717
  AHue: THue = nil; APartialHue: Boolean = False);
1718
var
1719
  i: Integer;
1720
  art: array of TArt;
1721
  caps: TGLTextureCaps;
1722
begin
1723
  inherited Create;
1724
1725
  FAnimData := AAnimData;
1726
1727
  FRealWidth := 0;
1728
  FRealHeight := 0;
1729
1730
  SetLength(FTextures, AAnimData.FrameCount);
1731
  SetLength(art, AAnimData.FrameCount);
1732
1733
  for i := 0 to AAnimData.FrameCount - 1 do
1734
  begin
1735
    art[i] := ResMan.Art.GetArt(ABaseID + AAnimData.FrameData[i], 0, AHue,
1736
      APartialHue);
1737
1738
    if art[i].Graphic.Width > FRealWidth then
1739
      FRealWidth := art[i].Graphic.Width;
1740
    if art[i].Graphic.Height > FRealHeight then
1741
      FRealHeight := art[i].Graphic.Height;
1742
  end;
1743
1744
  GetGLTextureCaps(caps);
1745
  CalculateTextureDimensions(caps, FRealWidth, FRealHeight, FWidth, FHeight);
1746
  FGraphic := TMultiImage.CreateFromParams(FWidth, FHeight, ifA8R8G8B8,
1747
    AAnimData.FrameCount);
1748
1749
  for i := 0 to AAnimData.FrameCount - 1 do
1750
  begin
1751
    FGraphic.ActiveImage := i;
1752
    art[i].Graphic.CopyTo(0, 0, art[i].Graphic.Width, art[i].Graphic.Height,
1753
      FGraphic, 0, 0);
1754
    FTextures[i] := GenerateTexture(FGraphic);
1755
    art[i].Free;
1756
  end;
1757
1758
  FGraphic.ActiveImage := 0;
1759
  FActiveFrame := 0;
1760
  FNextChange := GetTickCount + AAnimData.FrameStart * 100;
1761
end;
1762
1763
destructor TAnimMaterial.Destroy;
1764
begin
1765
  glDeleteTextures(Length(FTextures), @FTextures[0]);
1766
  inherited Destroy;
1767
end;
1768
1769
function TAnimMaterial.GetTexture: GLuint;
1770
begin
1771
  if FNextChange <= GetTickCount then
1772
  begin
1773
    FActiveFrame := (FActiveFrame + 1) mod FAnimData.FrameCount;
1774
    FGraphic.ActiveImage := FActiveFrame;
1775
1776
    if FActiveFrame = 0 then
1777
      FNextChange := GetTickCount + FAnimData.FrameStart * 100
1778
    else
1779
      FNextChange:= GetTickCount + FAnimData.FrameInterval * 100;
1780
  end;
1781
1782
  Result := FTextures[FActiveFrame];
1783
end;
1784
1785
end.
1786