Revision 157:0b95089e72d4 Client/ULandscape.pas

b/Client/ULandscape.pas
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

  
255
  PBlockInfo = ^TBlockInfo;
256
  TBlockInfo = record
257
    ScreenRect: TRect;
258
    DrawQuad: array[0..3,0..1] of TGLint;
259
    RealQuad: array[0..3,0..1] of TGLint;
260
    Item: TWorldItem;
261
    HighRes: TMaterial;
262
    LowRes: TMaterial;
263
    Normals: PNormals;
264
    State: TScreenState;
265
    Highlighted: Boolean;
266
    HueOverride: Boolean;
267
    CheckRealQuad: Boolean;
268
    Translucent: Boolean;
269
    Text: TGLText;
270
    Next: PBlockInfo;
271
  end;
272

  
273
  { TScreenBuffer }
274

  
275
  TScreenBuffer = class
276
    constructor Create; virtual;
277
    destructor Destroy; override;
278
  protected
279
    { Members }
280
    FCount: Cardinal;
281
    FShortCuts: array[-1..10] of PBlockInfo; //-1 = last, 0 = first, 1..10 = other shortcuts
282
    FShortCutsValid: Boolean;
283
    FSerial: Cardinal;
284
  public
285
    { Methods }
286
    function Add(AItem: TWorldItem): PBlockInfo;
287
    procedure Clear;
288
    procedure Delete(AItem: TWorldItem);
289
    function Find(AScreenPosition: TPoint): PBlockInfo;
290
    function GetSerial: Cardinal;
291
    function Insert(AItem: TWorldItem): PBlockInfo;
292
    function Iterate(var ABlockInfo: PBlockInfo): Boolean;
293
    procedure UpdateShortcuts;
294
    function UpdateSortOrder(AItem: TWorldItem): PBlockInfo;
295
    { Events }
296
    procedure OnTileRemoved(ATile: TMulBlock);
297
  end;
298
  
299
  TStaticInfo = packed record
300
    X: Word;
301
    Y: Word;
302
    Z: ShortInt;
303
    TileID: Word;
304
    Hue: Word;
305
  end;
306

  
307
implementation
308

  
309
uses
310
  UGameResources, UdmNetwork, UPackets, UPacketHandlers, Logging;
311

  
312
function GetID(AX, AY: Word): Integer; inline;
313
begin
314
  Result := (AX shl 16) or AY;
315
end;
316

  
317
operator := (AVector: Tvector3_single) GLVector: TGlVector3f;
318
begin
319
  GLVector[0] := AVector.data[0];
320
  GLVector[1] := AVector.data[1];
321
  GLVector[2] := AVector.data[2];
322
end;
323

  
324
{ TLandTextureManager }
325

  
326
constructor TLandTextureManager.Create;
327
begin
328
  inherited Create;
329
  FArtCache := TMaterialCache.Create(1024);
330
  FTexCache := TMaterialCache.Create(128);
331
  FAnimCache := TMaterialCache.Create(128);
332
  FUseAnims := True;
333
end;
334

  
335
destructor TLandTextureManager.Destroy;
336
begin
337
  FreeAndNil(FArtCache);
338
  FreeAndNil(FTexCache);
339
  FreeAndNil(FAnimCache);
340
  inherited Destroy;
341
end;
342

  
343
function TLandTextureManager.GetArtMaterial(ATileID: Word): TMaterial;
344
var
345
  artEntry: TArt;
346
  animData: TAnimData;
347
begin
348
  Result := nil;
349

  
350
  if FUseAnims and (ATileID >= $4000) and (tdfAnimation in
351
      ResMan.Tiledata.StaticTiles[ATileID - $4000].Flags) then
352
  begin
353
    animData := ResMan.Animdata.AnimData[ATileID - $4000];
354
    if (animData.FrameCount > 0) and not FAnimCache.QueryID(ATileID, Result) then
355
    begin
356
      Result := TAnimMaterial.Create(ATileID, animData);
357
      FAnimCache.StoreID(ATileID, Result);
358
    end;
359
  end;
360

  
361
  if (Result = nil) and not FArtCache.QueryID(ATileID, Result) then
362
  begin
363
    artEntry := TArt(ResMan.Art.Block[ATileID]);
364

  
365
    Result := TSimpleMaterial.Create(artEntry.Graphic);
366
    FArtCache.StoreID(ATileID, Result);
367

  
368
    artEntry.Free;
369
  end;
370

  
371
  Result.AddRef;
372
end;
373

  
374
function TLandTextureManager.GetArtMaterial(ATileID: Word; AHue: THue;
375
  APartialHue: Boolean): TMaterial;
376
var
377
  artEntry: TArt;
378
  animData: TAnimData;
379
  id: Integer;
380
begin
381
  if AHue = nil then
382
  begin
383
    Result := GetArtMaterial(ATileID);
384
  end else
385
  begin
386
    Result := nil;
387
    id := ATileID or (((AHue.ID + 1) and $3FFF) shl 16) or (Byte(APartialHue) shl 30);
388

  
389
    if FUseAnims and (ATileID >= $4000) and (tdfAnimation in
390
      ResMan.Tiledata.StaticTiles[ATileID - $4000].Flags) then
391
    begin
392
      animData := ResMan.Animdata.AnimData[ATileID - $4000];
393
      if (animData.FrameCount > 0) and not FAnimCache.QueryID(id, Result) then
394
      begin
395
        Result := TAnimMaterial.Create(ATileID, animData, AHue, APartialHue);
396
        FAnimCache.StoreID(id, Result);
397
      end;
398
    end;
399

  
400
    if (Result = nil) and not FArtCache.QueryID(id, Result) then
401
    begin
402
      artEntry := ResMan.Art.GetArt(ATileID, 0, AHue, APartialHue);
403

  
404
      Result := TSimpleMaterial.Create(artEntry.Graphic);
405
      FArtCache.StoreID(id, Result);
406

  
407
      artEntry.Free;
408
    end;
409
    Result.AddRef;
410
  end;
411
end;
412

  
413
function TLandTextureManager.GetStaticMaterial(AStaticItem: TStaticItem;
414
  AOverrideHue: Integer = -1): TMaterial;
415
var
416
  staticTiledata: TStaticTiledata;
417
  hue: THue;
418
begin
419
  staticTiledata := ResMan.Tiledata.StaticTiles[AStaticItem.TileID];
420
  if AOverrideHue < 0 then
421
    AOverrideHue := AStaticItem.Hue;
422

  
423
  if AOverrideHue > 0 then
424
    hue := ResMan.Hue.Hues[AOverrideHue - 1]
425
  else
426
    hue := nil;
427

  
428
  Result := GetArtMaterial($4000 + AStaticItem.TileID, hue,
429
    tdfPartialHue in staticTiledata.Flags);
430
end;
431

  
432
function TLandTextureManager.GetTexMaterial(ATileID: Word): TMaterial;
433
var
434
  texEntry: TTexture;
435
  texID: Integer;
436
begin
437
  if not FTexCache.QueryID(ATileID, Result) then
438
  begin
439
    texID := ResMan.Tiledata.LandTiles[ATileID].TextureID;
440
    if texID > 0 then
441
    begin
442
      texEntry := TTexture(ResMan.Texmaps.Block[texID]);
443

  
444
      Result := TSimpleMaterial.Create(texEntry.Graphic);
445
      FTexCache.StoreID(ATileID, Result);
446

  
447
      texEntry.Free;
448
    end else
449
      Result := nil;
450
  end;
451

  
452
  if Result <> nil then
453
    Result.AddRef;
454
end;
455

  
456
{ TSeperatedStaticBlock }
457

  
458
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex;
459
  AX, AY: Word);
460
var
461
  i: Integer;
462
  item: TStaticItem;
463
  block: TMemoryStream;
464
begin
465
  inherited Create;
466
  FItems := TStaticItemList.Create(False);
467

  
468
  FX := AX;
469
  FY := AY;
470

  
471
  for i := 0 to 63 do
472
    Cells[i] := TStaticItemList.Create;
473

  
474
  if (AData <> nil) and (AIndex.Lookup > 0) and (AIndex.Size > 0) then
475
  begin
476
    AData.Position := AIndex.Lookup;
477
    block := TMemoryStream.Create;
478
    block.CopyFrom(AData, AIndex.Size);
479
    block.Position := 0;
480
    for i := 1 to (AIndex.Size div 7) do
481
    begin
482
      item := TStaticItem.Create(Self, block, AX, AY);
483
      Cells[(item.Y mod 8) * 8 + (item.X mod 8)].Add(item);
484
    end;
485
    block.Free;
486
  end;
487
end;
488

  
489
constructor TSeperatedStaticBlock.Create(AData: TStream; AIndex: TGenericIndex);
490
begin
491
  Create(AData, AIndex, 0, 0);
492
end;
493

  
494
destructor TSeperatedStaticBlock.Destroy;
495
var
496
  i: Integer;
497
begin
498
  FreeAndNil(FItems);
499

  
500
  for i := 0 to 63 do
501
  begin
502
    if Cells[i] <> nil then
503
      FreeAndNil(Cells[i]);
504
  end;
505

  
506
  inherited Destroy;
507
end;
508

  
509
function TSeperatedStaticBlock.Clone: TSeperatedStaticBlock;
510
begin
511
  raise Exception.Create('TSeperatedStaticBlock.Clone is not implemented (yet).');
512
  Result := nil;
513
end;
514

  
515
function TSeperatedStaticBlock.GetSize: Integer;
516
begin
517
  RebuildList;
518
  Result := inherited GetSize;
519
end;
520

  
521
procedure TSeperatedStaticBlock.RebuildList;
522
var
523
  i, j, solver: Integer;
524
begin
525
  FItems.Clear;
526
  solver := 0;
527
  for i := 0 to 63 do
528
  begin
529
    if Cells[i] <> nil then
530
    begin
531
      for j := 0 to Cells[i].Count - 1 do
532
      begin
533
        FItems.Add(Cells[i].Items[j]);
534
        TStaticItem(Cells[i].Items[j]).UpdatePriorities(
535
          ResMan.Tiledata.StaticTiles[TStaticItem(Cells[i].Items[j]).TileID],
536
          solver);
537
        Inc(solver);
538
      end;
539
    end;
540
  end;
541
  Sort;
542
end;
543

  
544
{ TBlock }
545

  
546
constructor TBlock.Create(AMap: TMapBlock; AStatics: TStaticBlock);
547
begin
548
  inherited Create;
549
  FMapBlock := AMap;
550
  FStaticBlock := AStatics;
551
end;
552

  
553
destructor TBlock.Destroy;
554
begin
555
  if FMapBlock <> nil then FreeAndNil(FMapBlock);
556
  if FStaticBlock <> nil then FreeAndNil(FStaticBlock);
557
  inherited Destroy;
558
end;
559

  
560
procedure TBlock.UpdateBlockAcess(ALandscape: TLandscape);
561
var
562
  staticItem: TStaticItem;
563
  i: Integer;
564
begin
565
  for i := Low(FMapBlock.Cells) to High(FMapBlock.Cells) do
566
  begin
567
    FMapBlock.Cells[i].CanBeEdited := ALandscape.CanWrite(FMapBlock.Cells[i].X,
568
      FMapBlock.Cells[i].Y);
569
  end;
570

  
571
  if FStaticBlock is TSeperatedStaticBlock then
572
    TSeperatedStaticBlock(FStaticBlock).RebuildList; //fill items
573

  
574
  for i := 0 to FStaticBlock.Items.Count - 1 do
575
  begin
576
    staticItem := FStaticBlock.Items[i];
577
    staticItem.CanBeEdited := ALandscape.CanWrite(staticItem.X,
578
      staticItem.Y);
579
  end;
580
end;
581

  
582
{ TLandscape }
583

  
584
constructor TLandscape.Create(AWidth, AHeight: Word);
585
var
586
  i: Integer;
587
begin
588
  inherited Create;
589
  FWidth := AWidth;
590
  FHeight := AHeight;
591
  FCellWidth := FWidth * 8;
592
  FCellHeight := FHeight * 8;
593
  FBlockCache := TBlockCache.Create(256);
594
  FBlockCache.OnRemoveObject := @OnRemoveCachedObject;
595

  
596
  FOnChange := nil;
597
  FOnNewBlock := nil;
598
  FOnStaticDeleted := nil;
599
  FOnStaticElevated := nil;
600
  FOnStaticHued := nil;
601
  FOnStaticInserted := nil;
602

  
603
  FOpenRequests := TBits.Create(FWidth * FHeight);
604
  FOpenRequests.Clearall; //set all to 0
605
  FWriteMap := TBits.Create(FCellWidth * FCellHeight);
606
  for i := 0 to FWriteMap.Size - 1 do
607
    FWriteMap[i] := True;
608

  
609
  FMaxStaticID := Min(Min(ResMan.Animdata.AnimCount, ResMan.Tiledata.StaticCount),
610
    ResMan.Art.EntryCount - $4000);
611
  Logger.Send([lcClient, lcInfo], 'Landscape recognizes $%x StaticTile IDs.',
612
    [FMaxStaticId]);
613

  
614
  FDrawMap := TBits.Create($4000 + FMaxStaticID);
615
  for i := 0 to FDrawMap.Size - 1 do
616
    FDrawMap[i] := True;
617

  
618
  RegisterPacketHandler($04, TPacketHandler.Create(0, @OnBlocksPacket));
619
  RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket));
620
  RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket));
621
  RegisterPacketHandler($08, TPacketHandler.Create(10, @OnDeleteStaticPacket));
622
  RegisterPacketHandler($09, TPacketHandler.Create(11, @OnElevateStaticPacket));
623
  RegisterPacketHandler($0A, TPacketHandler.Create(14, @OnMoveStaticPacket));
624
  RegisterPacketHandler($0B, TPacketHandler.Create(12, @OnHueStaticPacket));
625
end;
626

  
627
destructor TLandscape.Destroy;
628
begin
629
  if FBlockCache <> nil then
630
  begin
631
    FBlockCache.OnRemoveObject := nil;
632
    FreeAndNil(FBlockCache);
633
  end;
634

  
635
  FreeAndNil(FOpenRequests);
636
  FreeAndNil(FWriteMap);
637
  FreeAndNil(FDrawMap);
638
  
639
  RegisterPacketHandler($04, nil);
640
  RegisterPacketHandler($06, nil);
641
  RegisterPacketHandler($07, nil);
642
  RegisterPacketHandler($08, nil);
643
  RegisterPacketHandler($09, nil);
644
  RegisterPacketHandler($0A, nil);
645
  RegisterPacketHandler($0B, nil);
646
  
647
  inherited Destroy;
648
end;
649

  
650
function TLandscape.GetMapBlock(AX, AY: Word): TMapBlock;
651
var
652
  block: TBlock;
653
begin
654
  Result := nil;
655
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
656
  begin
657
    if FBlockCache.QueryID(GetID(AX, AY), block) then
658
      Result := block.Map;
659
  end;
660
end;
661

  
662
function TLandscape.GetMapCell(AX, AY: Word): TMapCell;
663
var
664
  block: TMapBlock;
665
begin
666
  Result := nil;
667
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
668
  begin
669
    block := GetMapBlock(AX div 8, AY div 8);
670
    if block <> nil then
671
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
672
  end;
673
end;
674

  
675
function TLandscape.GetNormals(AX, AY: Word): TNormals;
676
begin
677
  GetNormals(AX, AY, Result);
678
end;
679

  
680
function TLandscape.GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
681
var
682
  block: TBlock;
683
begin
684
  Result := nil;
685
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
686
  begin
687
    if FBlockCache.QueryID(GetID(AX, AY), block) then
688
      Result := TSeperatedStaticBlock(block.Static);
689
  end;
690
end;
691

  
692
function TLandscape.GetStaticList(AX, AY: Word): TStaticItemList;
693
var
694
  block: TSeperatedStaticBlock;
695
begin
696
  Result := nil;
697
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
698
  begin
699
    block := GetStaticBlock(AX div 8, AY div 8);
700
    if block <> nil then
701
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
702
  end;
703
end;
704

  
705
procedure TLandscape.OnRemoveCachedObject(ABlock: TBlock);
706
begin
707
  if ABlock <> nil then
708
    dmNetwork.Send(TFreeBlockPacket.Create(ABlock.Map.X, ABlock.Map.Y));
709
end;
710

  
711
procedure TLandscape.OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
712
var
713
  index: TGenericIndex;
714
  map: TMapBlock;
715
  statics: TStaticBlock;
716
  coords: TBlockCoords;
717
  count: Word;
718
  id: Integer;
719
  block: TBlock;
720
begin
721
  index := TGenericIndex.Create(nil);
722
  while ABuffer.Position < ABuffer.Size do
723
  begin
724
    ABuffer.Read(coords, SizeOf(TBlockCoords));
725
    id := GetID(coords.X, coords.Y);
726

  
727
    map := TMapBlock.Create(ABuffer, coords.X, coords.Y);
728
    count := ABuffer.ReadWord;
729
    if count > 0 then
730
      index.Lookup := ABuffer.Position
731
    else
732
      index.Lookup := -1;
733
    index.Size := count * 7;
734
    statics := TSeperatedStaticBlock.Create(ABuffer, index, coords.X, coords.Y);
735

  
736
    FBlockCache.RemoveID(id);
737
    block := TBlock.Create(map, statics);
738
    block.UpdateBlockAcess(Self);
739
    FBlockCache.StoreID(id, block);
740

  
741
    FOpenRequests[coords.Y * FWidth + coords.X] := False;
742

  
743
    if Assigned(FOnNewBlock) then FOnNewBlock(block);
744
  end;
745
  index.Free;
746
end;
747

  
748
procedure TLandscape.OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
749
var
750
  x, y: Word;
751
  cell: TMapCell;
752
begin
753
  x := ABuffer.ReadWord;
754
  y := ABuffer.ReadWord;
755
  cell := GetMapCell(x, y);
756
  if cell <> nil then
757
  begin
758
    cell.Altitude := ABuffer.ReadShortInt;
759
    cell.TileID := ABuffer.ReadWord;
760
    if Assigned(FOnMapChanged) then FOnMapChanged(cell);
761
  end;
762
end;
763

  
764
procedure TLandscape.OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
765
var
766
  x, y: Word;
767
  block: TSeperatedStaticBlock;
768
  staticItem: TStaticItem;
769
  targetStaticList: TStaticItemList;
770
  i: Integer;
771
begin
772
  x := ABuffer.ReadWord;
773
  y := ABuffer.ReadWord;
774
  block := GetStaticBlock(x div 8, y div 8);
775
  if block <> nil then
776
  begin
777
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
778
    staticItem.X := x;
779
    staticItem.Y := y;
780
    staticItem.Z := ABuffer.ReadShortInt;
781
    staticItem.TileID := ABuffer.ReadWord;
782
    staticItem.Hue := ABuffer.ReadWord;
783
    targetStaticList := block.Cells[(y mod 8) * 8 + x mod 8];
784
    targetStaticList.Add(staticItem);
785
    for i := 0 to targetStaticList.Count - 1 do
786
      targetStaticList.Items[i].UpdatePriorities(
787
        ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
788
        i);
789
    targetStaticList.Sort(@CompareStaticItems);
790
    staticItem.Owner := block;
791
    staticItem.CanBeEdited := CanWrite(x, y);
792

  
793
    if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
794
  end;
795
end;
796

  
797
procedure TLandscape.OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
798
var
799
  block: TSeperatedStaticBlock;
800
  i: Integer;
801
  statics: TStaticItemList;
802
  staticInfo: TStaticInfo;
803
  staticItem: TStaticItem;
804
begin
805
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
806
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
807
  if block <> nil then
808
  begin
809
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
810
    for i := 0 to statics.Count - 1 do
811
    begin
812
      staticItem := statics.Items[i];
813
      if (staticItem.Z = staticInfo.Z) and
814
         (staticItem.TileID = staticInfo.TileID) and
815
         (staticItem.Hue = staticInfo.Hue) then
816
      begin
817
        if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
818
        staticItem.Delete;
819
        statics.Delete(i);
820

  
821
        Break;
822
      end;
823
    end;
824
  end;
825
end;
826

  
827
procedure TLandscape.OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
828
var
829
  block: TSeperatedStaticBlock;
830
  i,j : Integer;
831
  statics: TStaticItemList;
832
  staticInfo: TStaticInfo;
833
  staticItem: TStaticItem;
834
begin
835
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
836
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
837
  if block <> nil then
838
  begin
839
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
840
    for i := 0 to statics.Count - 1 do
841
    begin
842
      staticItem := statics.Items[i];
843
      if (staticItem.Z = staticInfo.Z) and
844
         (staticItem.TileID = staticInfo.TileID) and
845
         (staticItem.Hue = staticInfo.Hue) then
846
      begin
847
        staticItem.Z := ABuffer.ReadShortInt;
848
        for j := 0 to statics.Count - 1 do
849
          statics.Items[j].UpdatePriorities(
850
            ResMan.Tiledata.StaticTiles[statics.Items[j].TileID],
851
            j);
852
        statics.Sort(@CompareStaticItems);
853

  
854
        if Assigned(FOnStaticElevated) then FOnStaticElevated(staticItem);
855

  
856
        Break;
857
      end;
858
    end;
859
  end;
860
end;
861

  
862
procedure TLandscape.OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
863
var
864
  sourceBlock, targetBlock: TSeperatedStaticBlock;
865
  i: Integer;
866
  statics: TStaticItemList;
867
  staticInfo: TStaticInfo;
868
  staticItem: TStaticItem;
869
  newX, newY: Word;
870
begin
871
  staticItem := nil;
872
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
873
  newX := EnsureRange(ABuffer.ReadWord, 0, FCellWidth - 1);
874
  newY := EnsureRange(ABuffer.ReadWord, 0, FCellHeight - 1);
875

  
876
  sourceBlock := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
877
  targetBlock := GetStaticBlock(newX div 8, newY div 8);
878
  if sourceBlock <> nil then
879
  begin
880
    statics := sourceBlock.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
881
    i := 0;
882
    while (i < statics.Count) and (staticItem = nil) do
883
    begin
884
      staticItem := statics.Items[i];
885
      if (staticItem.Z <> staticInfo.Z) or
886
         (staticItem.TileID <> staticInfo.TileID) or
887
         (staticItem.Hue <> staticInfo.Hue) then
888
      begin
889
        staticItem := nil;
890
      end;
891
      Inc(i);
892
    end;
893

  
894
    if staticItem <> nil then
895
    begin
896
      if Assigned(FOnStaticDeleted) then FOnStaticDeleted(staticItem);
897
      staticItem.Delete;
898
      statics.Remove(staticItem);
899
    end;
900
  end;
901

  
902
  if targetBlock <> nil then
903
  begin
904
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
905
    staticItem.X := newX;
906
    staticItem.Y := newY;
907
    staticItem.Z := staticInfo.Z;
908
    staticItem.TileID := staticInfo.TileID;
909
    staticItem.Hue := staticInfo.Hue;
910
    statics := targetBlock.Cells[(newY mod 8) * 8 + newX mod 8];
911
    statics.Add(staticItem);
912
    for i := 0 to statics.Count - 1 do
913
      TStaticItem(statics.Items[i]).UpdatePriorities(
914
        ResMan.Tiledata.StaticTiles[TStaticItem(statics.Items[i]).TileID],
915
        i);
916
    statics.Sort(@CompareStaticItems);
917
    staticItem.Owner := targetBlock;
918
    staticItem.CanBeEdited := CanWrite(newX, newY);
919

  
920
    if Assigned(FOnStaticInserted) then FOnStaticInserted(staticItem);
921
  end;
922
end;
923

  
924
procedure TLandscape.OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
925
var
926
  block: TSeperatedStaticBlock;
927
  i : Integer;
928
  statics: TStaticItemList;
929
  staticInfo: TStaticInfo;
930
  staticItem: TStaticItem;
931
begin
932
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
933
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
934
  if block <> nil then
935
  begin
936
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
937
    for i := 0 to statics.Count - 1 do
938
    begin
939
      staticItem := statics.Items[i];
940
      if (staticItem.Z = staticInfo.Z) and
941
         (staticItem.TileID = staticInfo.TileID) and
942
         (staticItem.Hue = staticInfo.Hue) then
943
      begin
944
        staticItem.Hue := ABuffer.ReadWord;
945
        if Assigned(FOnStaticHued) then FOnStaticHued(staticItem);
946
        Break;
947
      end;
948
    end;
949
  end;
950
end;
951

  
952
function TLandscape.CanWrite(AX, AY: Word): Boolean;
953
begin
954
  Result := FWriteMap[AX * FCellHeight + AY];
955
end;
956

  
957
procedure TLandscape.FillDrawList(ADrawList: TScreenBuffer; AX, AY, AWidth,
958
  AHeight: Word; AMap, AStatics: Boolean; ANoDraw: Boolean;
959
  AAdditionalTiles: TWorldItemList = nil);
960
var
961
  drawMapCell: TMapCell;
962
  drawStatics: TStaticItemList;
963
  i, x, y: Integer;
964
  tempDrawList: TWorldItemList;
965
  staticTileData: TStaticTiledata;
966
begin
967
  ADrawList.Clear;
968
  tempDrawList := TWorldItemList.Create(False);;
969
  for x := AX to AX + AWidth do
970
  begin
971
    for y := AY to AY + AWidth do
972
    begin
973
      if AMap then
974
      begin
975
        drawMapCell := GetMapCell(x, y);
976
        if (drawMapCell <> nil) and (ANoDraw or FDrawMap[drawMapCell.TileID]) then
977
        begin
978
          drawMapCell.Priority := GetEffectiveAltitude(drawMapCell);
979
          drawMapCell.PriorityBonus := 0;
980
          drawMapCell.PrioritySolver := 0;
981
          tempDrawList.Add(drawMapCell);
982
        end;
983
      end;
984

  
985
      if AStatics then
986
      begin
987
        drawStatics := GetStaticList(x, y);
988
        if drawStatics <> nil then
989
          for i := 0 to drawStatics.Count - 1 do
990
          begin
991
            staticTileData := ResMan.Tiledata.StaticTiles[drawStatics[i].TileID];
992
            if ANoDraw or FDrawMap[drawStatics[i].TileID + $4000] then
993
            begin
994
              drawStatics[i].UpdatePriorities(staticTileData,
995
                ADrawList.GetSerial);
996
              tempDrawList.Add(drawStatics[i]);
997
            end;
998
          end;
999
      end;
1000
    end;
1001
  end;
1002

  
1003
  for i := 0 to AAdditionalTiles.Count - 1 do
1004
    tempDrawList.Add(AAdditionalTiles[i]);
1005

  
1006
  tempDrawList.Sort(@CompareWorldItems);
1007
  for i := 0 to tempDrawList.Count - 1 do
1008
    ADrawList.Add(TWorldItem(tempDrawList[i]));
1009
  tempDrawList.Free;
1010
end;
1011

  
1012
function TLandscape.GetEffectiveAltitude(ATile: TMapCell): ShortInt;
1013
var
1014
  north, west, south, east: ShortInt;
1015
begin
1016
  north := ATile.Altitude;
1017
  west := GetLandAlt(ATile.X, ATile.Y + 1, north);
1018
  south := GetLandAlt(ATile.X + 1, ATile.Y + 1, north);
1019
  east := GetLandAlt(ATile.X + 1, ATile.Y, north);
1020

  
1021
  if Abs(north - south) >= Abs(west - east) then
1022
    Result := Min(north, south) + Abs(west - east) div 2
1023
  else
1024
    Result := Min(north, south) + Abs(north - south) div 2;
1025
end;
1026

  
1027
function TLandscape.GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
1028
var
1029
  cell: TMapCell;
1030
begin
1031
  cell := MapCell[AX, AY];
1032
  if cell <> nil then
1033
    Result := cell.Altitude
1034
  else
1035
    Result := ADefault;
1036
end;
1037

  
1038
procedure TLandscape.GetNormals(AX, AY: Word; var ANormals: TNormals);
1039
type
1040
  _Normals = array[0..3] of Tvector3_single;
1041
var
1042
  cells: array[0..2, 0..2] of _Normals;
1043
  north, west, south, east: Tvector3_single;
1044
  i, j: Integer;
1045

  
1046
  function Normalize(const AVector: Tvector3_single): Tvector3_single; inline;
1047
  begin
1048
    Result := AVector / AVector.length;
1049
  end;
1050

  
1051
  function GetPlainNormals(X, Y: SmallInt): _Normals;
1052
  var
1053
    cell: TMapCell;
1054
    north, west, south, east: ShortInt;
1055
    u, v: Tvector3_single;
1056
  begin
1057
    cell := GetMapCell(X, Y);
1058
    if cell <> nil then
1059
    begin
1060
      north := cell.Altitude;
1061
      west := GetLandAlt(cell.X, cell.Y + 1, north);
1062
      south := GetLandAlt(cell.X + 1, cell.Y + 1, north);
1063
      east := GetLandAlt(cell.X + 1, cell.Y, north);
1064
    end else
1065
    begin
1066
      north := 0;
1067
      west := 0;
1068
      east := 0;
1069
      south := 0;
1070
    end;
1071

  
1072
    if (north = west) and (west = east) and (north = south) then
1073
    begin
1074
      Result[0].init(0, 0, 1);
1075
      Result[1].init(0, 0, 1);
1076
      Result[2].init(0, 0, 1);
1077
      Result[3].init(0, 0, 1);
1078
    end else
1079
    begin
1080
      u.init(-22, 22, (north - east) * 4);
1081
      v.init(-22, -22, (west - north) * 4);
1082
      Result[0] := Normalize(u >< v);
1083

  
1084
      u.init(22, 22, (east - south) * 4);
1085
      v.init(-22, 22, (north - east) * 4);
1086
      Result[1] := Normalize(u >< v);
1087

  
1088
      u.init(22, -22, (south - west) * 4);
1089
      v.init(22, 22, (east - south) * 4);
1090
      Result[2] := Normalize(u >< v);
1091

  
1092
      u.init(-22, -22, (west - north) * 4);
1093
      v.init(22, -22, (south - west) * 4);
1094
      Result[3] := Normalize(u >< v);
1095
    end;
1096
  end;
1097
begin
1098
  for i := 0 to 2 do
1099
    for j := 0 to 2 do
1100
      cells[i, j] := GetPlainNormals(AX - 1 + i, AY - 1 + j);
1101

  
1102
  north := cells[0, 0][2];
1103
  west := cells[0, 1][1];
1104
  east := cells[1, 0][3];
1105
  south := cells[1, 1][0];
1106
  ANormals[0] := Normalize(north + west + east + south);
1107

  
1108
  north := cells[1, 0][2];
1109
  west := cells[1, 1][1];
1110
  east := cells[2, 0][3];
1111
  south := cells[2, 1][0];
1112
  ANormals[1] := Normalize(north + west + east + south);
1113

  
1114
  north := cells[1, 1][2];
1115
  west := cells[1, 2][1];
1116
  east := cells[2, 1][3];
1117
  south := cells[2, 2][0];
1118
  ANormals[2] := Normalize(north + west + east + south);
1119

  
1120
  north := cells[0, 1][2];
1121
  west := cells[0, 2][1];
1122
  east := cells[1, 1][3];
1123
  south := cells[1, 2][0];
1124
  ANormals[3] := Normalize(north + west + east + south);
1125
end;
1126

  
1127
procedure TLandscape.LoadNoDrawMap(AFileName: String);
1128
var
1129
  noDrawFile: TextFile;
1130
  line, ids1, ids2: String;
1131
  i, id1, id2, splitPos: Integer;
1132
begin
1133
  AssignFile(noDrawFile, AFileName);
1134
  Reset(noDrawFile);
1135
  while not EOF(noDrawFile) do
1136
  begin
1137
    ReadLn(noDrawFile, line);
1138
    if (Length(line) > 0) and (line[1] in ['S', 'T']) then
1139
    begin
1140
      splitPos := Pos('-', line);
1141
      if splitPos > 1 then
1142
      begin
1143
        ids1 := Copy(line, 2, splitPos - 2);
1144
        ids2 := Copy(line, splitPos + 1, Length(line));
1145
        if TryStrToInt(ids1, id1) and TryStrToInt(ids2, id2) then
1146
        begin
1147
          if line[1] = 'S' then
1148
          begin
1149
            Inc(id1, $4000);
1150
            Inc(id2, $4000);
1151
          end;
1152

  
1153
          for i := id1 to id2 do
1154
            if i < FDrawMap.Size then
1155
              FDrawMap[i] := False;
1156
        end;
1157
      end else
1158
      begin
1159
        ids1 := Copy(line, 2, Length(line));
1160
        if TryStrToInt(ids1, id1) then
1161
        begin
1162
          if line[1] = 'S' then
1163
            Inc(id1, $4000);
1164
          if id1 < FDrawMap.Size then
1165
            FDrawMap[id1] := False;
1166
        end;
1167
      end;
1168
    end;
1169
  end;
1170
  CloseFile(noDrawFile);
1171
end;
1172

  
1173
procedure TLandscape.MoveStatic(AStatic: TStaticItem; AX, AY: Word);
1174
var
1175
  sourceBlock, targetBlock: TSeperatedStaticBlock;
1176
  targetStaticList: TStaticItemList;
1177
  i: Integer;
1178
begin
1179
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
1180
  begin
1181
    sourceBlock := AStatic.Owner as TSeperatedStaticBlock;
1182
    targetBlock := GetStaticBlock(AX div 8, AY div 8);
1183
    if (sourceBlock <> nil) and (targetBlock <> nil) then
1184
    begin
1185
      sourceBlock.Cells[(AStatic.Y mod 8) * 8 + AStatic.X mod 8].Remove(AStatic);
1186
      targetStaticList := targetBlock.Cells[(AY mod 8) * 8 + AX mod 8];
1187
      targetStaticList.Add(AStatic);
1188
      for i := 0 to targetStaticList.Count - 1 do
1189
        targetStaticList.Items[i].UpdatePriorities(
1190
          ResMan.Tiledata.StaticTiles[targetStaticList.Items[i].TileID],
1191
          i);
1192
      targetStaticList.Sort(@CompareStaticItems);
1193
      AStatic.UpdatePos(AX, AY, AStatic.Z);
1194
      AStatic.Owner := targetBlock;
1195
    end;
1196
  end;
1197
end;
1198

  
1199
procedure TLandscape.PrepareBlocks(AX1, AY1, AX2, AY2: Word);
1200
var
1201
  x, y, i: Integer;
1202
  coords: TBlockCoordsArray;
1203
  block: TBlock;
1204
begin
1205
  AX1 := EnsureRange(AX1, 0, FWidth - 1);
1206
  AY1 := EnsureRange(AY1, 0, FHeight - 1);
1207
  AX2 := EnsureRange(AX2, 0, FWidth - 1);
1208
  AY2 := EnsureRange(AY2, 0, FHeight - 1);
1209

  
1210
  SetLength(coords, 0);
1211
  for x := AX1 to AX2 do
1212
  begin
1213
    for y := AY1 to AY2 do
1214
    begin
1215
      if (not FOpenRequests[y * FWidth + x]) and
1216
         (not FBlockCache.QueryID(GetID(x, y), block)) then
1217
      begin
1218
        SetLength(coords, Length(coords) + 1);
1219
        i := High(coords);
1220
        coords[i].X := x;
1221
        coords[i].Y := y;
1222
        FOpenRequests[y * FWidth + x] := True;
1223
      end;
1224
    end;
1225
  end;
1226
  if Length(coords) > 0 then
1227
    dmNetwork.Send(TRequestBlocksPacket.Create(coords));
1228
end;
1229

  
1230
procedure TLandscape.UpdateBlockAccess;
1231
var
1232
  cacheEntry: TBlockCache.PCacheEntry;
1233
begin
1234
  cacheEntry := nil;
1235
  while FBlockCache.Iterate(cacheEntry) do
1236
    if cacheEntry^.Obj <> nil then
1237
      cacheEntry^.Obj.UpdateBlockAcess(Self);
1238
end;
1239

  
1240
procedure TLandscape.UpdateWriteMap(AStream: TEnhancedMemoryStream);
1241
var
1242
  x1, y1, x2, y2: Word;
1243
  i, areaCount, cellX, cellY: Integer;
1244
begin
1245
  Logger.EnterMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
1246

  
1247
  areaCount := AStream.ReadWord;
1248
  Logger.Send([lcLandscape, lcDebug], 'AreaCount', areaCount);
1249

  
1250
  if areaCount > 0 then
1251
  begin
1252
    FWriteMap.Clearall;
1253
    for i := 0 to areaCount - 1 do
1254
    begin
1255
      x1 := AStream.ReadWord;
1256
      y1 := AStream.ReadWord;
1257
      x2 := AStream.ReadWord;
1258
      y2 := AStream.ReadWord;
1259
      for cellX := x1 to x2 do
1260
        for cellY := y1 to y2 do
1261
          FWriteMap[cellX * FCellHeight + cellY] := True;
1262
    end;
1263
  end else
1264
    for i := 0 to FWriteMap.Size - 1 do
1265
      FWriteMap[i] := True;
1266

  
1267
  Logger.Send([lcLandscape, lcDebug], 'WriteMap @ 0,0', FWriteMap[0]);
1268

  
1269
  UpdateBlockAccess;
1270
  Logger.ExitMethod([lcLandscape, lcDebug], 'TLandscape.UpdateWriteMap');
1271
end;
1272

  
1273
{ TMaterial }
1274

  
1275
constructor TMaterial.Create;
1276
begin
1277
  FRefCount := 1;
1278
end;
1279

  
1280
destructor TMaterial.Destroy;
1281
begin
1282
  FreeAndNil(FGraphic);
1283
  inherited Destroy;
1284
end;
1285

  
1286
class procedure TMaterial.CalculateTextureDimensions(ACaps: TGLTextureCaps;
1287
  ARealWidth, ARealHeight: Integer; out AWidth, AHeight: Integer);
1288
begin
1289
  if ACaps.NonPowerOfTwo then
1290
  begin
1291
    AWidth := ARealWidth;
1292
    AHeight := ARealHeight;
1293
  end else
1294
  begin
1295
    if IsPow2(ARealWidth) then
1296
      AWidth := ARealWidth
1297
    else
1298
      AWidth := NextPow2(ARealWidth);
1299

  
1300
    if IsPow2(ARealHeight) then
1301
      AHeight := ARealHeight
1302
    else
1303
      AHeight := NextPow2(ARealHeight);
1304
  end;
1305
end;
1306

  
1307
function TMaterial.GenerateTexture(AImage: TBaseImage): TGLuint;
1308
begin
1309
  Result := CreateGLTextureFromImage(AImage.ImageDataPointer^);
1310
  glBindTexture(GL_TEXTURE_2D, Result);
1311
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1312
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1313
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1314
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1315
end;
1316

  
1317
procedure TMaterial.AddRef;
1318
begin
1319
  Inc(FRefCount);
1320
end;
1321

  
1322
procedure TMaterial.DelRef;
1323
begin
1324
  Dec(FRefCount);
1325
  if FRefCount < 1 then
1326
    Free;
1327
end;
1328

  
1329
function TMaterial.HitTest(AX, AY: Integer): Boolean;
1330
var
1331
  pixel: TColor32Rec;
1332
begin
1333
  Result := False;
1334
  if InRange(AX, 0, FGraphic.Width - 1) and
1335
     InRange(AY, 0, FGraphic.Height - 1) then
1336
  begin
1337
    pixel := GetPixel32(FGraphic.ImageDataPointer^, AX, AY);
1338
    if pixel.A > 0 then
1339
      Result := True;
1340
  end;
1341
end;
1342

  
1343
function TMaterial.CanBeRemoved: Boolean;
1344
begin
1345
  Result := FRefCount <= 1;
1346
end;
1347

  
1348
procedure TMaterial.RemoveFromCache;
1349
begin
1350
  DelRef;
1351
end;
1352

  
1353
{ TScreenBuffer }
1354

  
1355
constructor TScreenBuffer.Create;
1356
begin
1357
  inherited Create;
1358
  FCount := 0;
1359
  FSerial := 0;
1360
  UpdateShortcuts;
1361
end;
1362

  
1363
destructor TScreenBuffer.Destroy;
1364
begin
1365
  Clear;
1366
  inherited Destroy;
1367
end;
1368

  
1369
function TScreenBuffer.Add(AItem: TWorldItem): PBlockInfo;
1370
begin
1371
  New(Result);
1372
  AItem.Locked := True;
1373
  AItem.OnDestroy.RegisterEvent(@OnTileRemoved);
1374
  Result^.Item := AItem;
1375
  Result^.HighRes := nil;
1376
  Result^.LowRes := nil;
1377
  Result^.Normals := nil;
1378
  Result^.State := ssNormal;
1379
  Result^.Highlighted := False;
1380
  Result^.Translucent := False;
1381
  Result^.Text := nil;
1382
  Result^.Next := nil;
1383

  
1384
  if FShortCuts[0] = nil then //First element
1385
  begin
1386
    FShortCuts[0] := Result;
1387
    FShortCuts[-1] := Result; //Last element
1388
  end else
1389
  begin
1390
    FShortCuts[-1]^.Next := Result;
1391
    FShortCuts[-1] := Result;
1392
  end;
1393

  
1394
  Inc(FCount);
1395
end;
1396

  
1397
procedure TScreenBuffer.Clear;
1398
var
1399
  current, next: PBlockInfo;
1400
begin
1401
  current := FShortCuts[0];
1402
  while current <> nil do
1403
  begin
1404
    next := current^.Next;
1405
    current^.Item.Locked := False;
1406
    current^.Item.OnDestroy.UnregisterEvent(@OnTileRemoved);
1407
    if current^.Normals <> nil then Dispose(current^.Normals);
1408
    if current^.HighRes <> nil then current^.HighRes.DelRef;
1409
    if current^.LowRes <> nil then current^.LowRes.DelRef;
1410
    current^.Text.Free;
1411
    Dispose(current);
1412
    current := next;
1413
  end;
1414
  FShortCuts[0] := nil;
1415
  FShortCuts[-1] := nil;
1416

  
1417
  FCount := 0;
1418
  FSerial := 0;
1419

  
1420
  UpdateShortcuts;
1421
end;
1422

  
1423
procedure TScreenBuffer.Delete(AItem: TWorldItem);
1424
var
1425
  current, last, next: PBlockInfo;
1426
begin
1427
  last := nil;
1428
  current := FShortCuts[0];
1429
  while current <> nil do
1430
  begin
1431
    if current^.Item = AItem then
1432
    begin
1433
      if FShortCuts[-1] = current then FShortCuts[-1] := last;
1434
      if FShortCuts[0] = current then FShortCuts[0] := current^.Next;
1435
      if last <> nil then last^.Next := current^.Next;
1436

  
1437
      if current^.Normals <> nil then Dispose(current^.Normals);
1438
      if current^.HighRes <> nil then current^.HighRes.DelRef;
1439
      if current^.LowRes <> nil then current^.LowRes.DelRef;
1440
      current^.Text.Free;
1441

  
1442
      Dispose(current);
1443
      Dec(FCount);
1444
      FShortCutsValid := False;
1445
      next := nil;
1446
    end else
1447
      next := current^.Next;
1448

  
1449
    last := current;
1450
    current := next;
1451
  end;
1452
end;
1453

  
1454
function TScreenBuffer.Find(AScreenPosition: TPoint): PBlockInfo;
1455
var
1456
  current: PBlockInfo;
1457
  buff: array[0..3] of GLuint;
1458
begin
1459
  Result := nil;
1460
  current := FShortCuts[0];
1461
  while current <> nil do //search the last matching tile
1462
  begin
1463
    if (current^.State = ssNormal) and
1464
       PtInRect(current^.ScreenRect, AScreenPosition)then
1465
    begin
1466
      if current^.CheckRealQuad then
1467
      begin
1468
        //OpenGL hit test
1469
        //We use the "real quad" here to prevent the draw-preview from
1470
        //intercepting with our actual tiles (which are "hidden" then).
1471
        glSelectBuffer(4, @buff[0]);
1472
        glViewport(current^.ScreenRect.Left, current^.ScreenRect.Top,
1473
          current^.ScreenRect.Right, current^.ScreenRect.Bottom);
1474
        glRenderMode(GL_SELECT);
1475
        glInitNames;
1476
        glPushName(0);
1477

  
1478
        glPushMatrix;
1479
          glMatrixMode(GL_PROJECTION);
1480
          glLoadIdentity;
1481
          gluOrtho2D(AScreenPosition.x, AScreenPosition.x + 1,
1482
            AScreenPosition.y + 1, AScreenPosition.y);
1483
          glMatrixMode(GL_MODELVIEW);
1484
          glLoadIdentity;
1485

  
1486
          glBegin(GL_QUADS);
1487
            glVertex2iv(@current^.RealQuad[0]);
1488
            glVertex2iv(@current^.RealQuad[3]);
1489
            glVertex2iv(@current^.RealQuad[2]);
1490
            glVertex2iv(@current^.RealQuad[1]);
1491
          glEnd;
1492
        glPopMatrix;
1493
        glFlush;
1494

  
1495
        if glRenderMode(GL_RENDER) > 0 then //glRenderMode now returns the number of hits
1496
          Result := current;
1497
      end else
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff