Statistics
| Branch: | Tag: | Revision:

root / Client / ULandscape.pas @ 0:95bd93c28625

History | View | Annotate | Download (33.3 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 2007 Andreas Schneider
25
 *)
26
unit ULandscape;
27
28
{$mode objfpc}{$H+}
29
30
interface
31
32
uses
33
  SysUtils, Classes, math, contnrs, LCLIntf, GL, GLU, ImagingOpenGL,
34
  Imaging, ImagingClasses, ImagingTypes, ImagingUtility,
35
  UGenericIndex, UMap, UStatics, UArt, UTexture, UTileData, UHue, UWorldItem,
36
  UMulBlock,
37
  UListSort, UVector, UEnhancedMemoryStream,
38
  UCacheManager, ULinkedList;
39
40
type
41
  TNormals = array[0..3] of TVector;
42
  PRadarBlock = ^TRadarBlock;
43
  TRadarBlock = array[0..7, 0..7] of Word;
44
  
45
  { TMaterial }
46
  
47
  TMaterial = class(TObject)
48
    constructor Create(AWidth, AHeight: Integer; AGraphic: TSingleImage);
49
    destructor Destroy; override;
50
  protected
51
    FWidth: Integer;
52
    FHeight: Integer;
53
    FRealWidth: Integer;
54
    FRealHeight: Integer;
55
    FTexture: GLuint;
56
    FGraphic: TSingleImage;
57
  public
58
    property Width: Integer read FWidth;
59
    property Height: Integer read FHeight;
60
    property RealWidth: Integer read FRealWidth;
61
    property RealHeight: Integer read FRealHeight;
62
    property Texture: GLuint read FTexture;
63
    property Graphic: TSingleImage read FGraphic;
64
    
65
    function HitTest(AX, AY: Integer): Boolean;
66
    procedure UpdateTexture;
67
  end;
68
  
69
  { TLandTextureManager }
70
  
71
  TLandTextureManager = class(TObject)
72
    constructor Create;
73
    destructor Destroy; override;
74
    function GetArtMaterial(ATileID: Word): TMaterial; overload;
75
    function GetArtMaterial(ATileID: Word; AHue: THue; APartialHue: Boolean): TMaterial; overload;
76
    function GetFlatLandMaterial(ATileID: Word): TMaterial;
77
    function GetTexMaterial(ATileID: Word): TMaterial;
78
  protected
79
    FArtCache: TCacheManager;
80
    FFlatLandArtCache: TCacheManager;
81
    FTexCache: TCacheManager;
82
  end;
83
  
84
  { TBlock }
85
86
  TBlock = class(TObject)
87
    constructor Create(AMap: TMapBlock; AStatics: TStaticBlock);
88
    destructor Destroy; override;
89
  protected
90
    FMapBlock: TMapBlock;
91
    FStaticBlock: TStaticBlock;
92
  public
93
    property Map: TMapBlock read FMapBlock;
94
    property Static: TStaticBlock read FStaticBlock;
95
  end;
96
  
97
  TLandscapeChangeEvent = procedure of object;
98
  TStaticFilter = function(AStatic: TStaticItem): Boolean of object;
99
100
  { TLandscape }
101
102
  TLandscape = class(TObject)
103
    constructor Create(AWidth, AHeight: Word);
104
    destructor Destroy; override;
105
  protected
106
    FWidth: Word;
107
    FHeight: Word;
108
    FCellWidth: Word;
109
    FCellHeight: Word;
110
    FBlockCache: TCacheManager;
111
    FOnChange: TLandscapeChangeEvent;
112
    FOpenRequests: array of Boolean;
113
    function Compare(left, right: TObject): Integer;
114
    function GetNormals(AX, AY: Word): TNormals;
115
    function GetMapCell(AX, AY: Word): TMapCell;
116
    function GetStaticList(AX, AY: Word): TList;
117
    function GetMapBlock(AX, AY: Word): TMapBlock;
118
    function GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
119
    procedure UpdateStaticsPriority(AStaticItem: TStaticItem;
120
      APrioritySolver: Integer);
121
    procedure OnBlockChanged(ABlock: TMulBlock);
122
    procedure OnRemoveCachedObject(AObject: TObject);
123
    
124
    procedure OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
125
    procedure OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
126
    procedure OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
127
    procedure OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
128
    procedure OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
129
    procedure OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
130
    procedure OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
131
  public
132
    property Width: Word read FWidth;
133
    property Height: Word read FHeight;
134
    property CellWidth: Word read FCellWidth;
135
    property CellHeight: Word read FCellHeight;
136
    property MapCell[X, Y: Word]: TMapCell read GetMapCell;
137
    property StaticList[X, Y: Word]: TList read GetStaticList;
138
    property Normals[X, Y: Word]: TNormals read GetNormals;
139
    property OnChange: TLandscapeChangeEvent read FOnChange write FOnChange;
140
141
    function GetDrawList(AX, AY: Word; AMinZ, AMaxZ: ShortInt;
142
      AGhostTile: TWorldItem; AVirtualLayer: TStaticItem; AMap,
143
      AStatics: Boolean; ANoDraw:Boolean; AStaticsFilter: TStaticFilter): TList;
144
    function GetEffectiveAltitude(ATile: TMapCell): ShortInt;
145
    function GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
146
147
    procedure MoveStatic(AStatic: TStaticItem; AX, AY: Word);
148
    procedure PrepareBlocks(AX1, AY1, AX2, AY2: Word);
149
  end;
150
  PBlockInfo = ^TBlockInfo;
151
  TBlockInfo = record
152
    ScreenRect: TRect;
153
    Item: TWorldItem;
154
    Material: TMaterial;
155
    Next: PBlockInfo;
156
  end;
157
158
  { TTileList }
159
160
  TTileList = class(TObject)
161
    constructor Create; virtual;
162
    destructor Destroy; override;
163
  protected
164
    FFirst: PBlockInfo;
165
    FLastBlock: PBlockInfo;
166
  public
167
    procedure Clear; virtual;
168
    function Iterate(var ABlockInfo: PBlockInfo): Boolean; virtual;
169
    procedure Add(AItem: TWorldItem); virtual;
170
    procedure Delete(AItem: TWorldItem); virtual;
171
    property LastBlock: PBlockInfo read FLastBlock;
172
  end;
173
174
  { TScreenBuffer }
175
176
  TScreenBuffer = class(TTileList)
177
  public
178
    procedure OnTileRemoved(ATile: TMulBlock);
179
    procedure Clear; override;
180
    function Find(AScreenPosition: TPoint): PBlockInfo;
181
    procedure Store(AScreenRect: TRect; AItem: TWorldItem; AMaterial: TMaterial);
182
  end;
183
  
184
  TStaticInfo = packed record
185
    X: Word;
186
    Y: Word;
187
    Z: ShortInt;
188
    TileID: Word;
189
    Hue: Word;
190
  end;
191
  //operator=(AStaticItem: TStaticItem; AStaticInfo: TStaticInfo): Boolean;
192
193
implementation
194
195
uses
196
  UGameResources, UdmNetwork, UPackets, UPacketHandlers;
197
198
const
199
  mMap = 0;
200
  mStatics = 1;
201
202
function GetID(AX, AY: Word): Integer;
203
begin
204
  Result := ((AX and $7FFF) shl 15) or (AY and $7FFF);
205
end;
206
207
{operator=(AStaticItem: TStaticItem; AStaticInfo: TStaticInfo): Boolean;
208
begin
209
  Result := (AStaticItem.X = AStaticInfo.X) and
210
    (AStaticItem.Y = AStaticInfo.Y) and
211
    (AStaticItem.Z = AStaticInfo.Z) and
212
    (AStaticItem.TileID = AStaticInfo.TileID) and
213
    (AStaticItem.Hue = AStaticInfo.Hue);
214
end;}
215
216
{ TLandTextureManager }
217
218
constructor TLandTextureManager.Create;
219
begin
220
  inherited Create;
221
  FArtCache := TCacheManager.Create(1024);
222
  FFlatLandArtCache := TCacheManager.Create(128);
223
  FTexCache := TCacheManager.Create(128);
224
end;
225
226
destructor TLandTextureManager.Destroy;
227
begin
228
  if FArtCache <> nil then FreeAndNil(FArtCache);
229
  if FFlatLandArtCache <> nil then FreeAndNil(FFlatLandArtCache);
230
  if FTexCache <> nil then FreeAndNil(FTexCache);
231
  inherited Destroy;
232
end;
233
234
function TLandTextureManager.GetArtMaterial(ATileID: Word): TMaterial;
235
var
236
  artEntry: TArt;
237
begin
238
  if not FArtCache.QueryID(ATileID, TObject(Result)) then
239
  begin
240
    artEntry := TArt(ResMan.Art.Block[ATileID]);
241
242
    Result := TMaterial.Create(artEntry.Graphic.Width, artEntry.Graphic.Height,
243
      artEntry.Graphic);
244
    FArtCache.StoreID(ATileID, Result);
245
246
    artEntry.Free;
247
  end;
248
end;
249
250
function TLandTextureManager.GetArtMaterial(ATileID: Word; AHue: THue; APartialHue: Boolean): TMaterial;
251
var
252
  artEntry: TArt;
253
  id: Integer;
254
begin
255
  if AHue = nil then
256
  begin
257
    Result := GetArtMaterial(ATileID);
258
  end else
259
  begin
260
    id := ATileID or ((AHue.ID and $3FFF) shl 15) or (Byte(APartialHue) shl 29);
261
    if not FArtCache.QueryID(id, TObject(Result)) then
262
    begin
263
      artEntry := ResMan.Art.GetArt(ATileID, 0, AHue, APartialHue);
264
265
      Result := TMaterial.Create(artEntry.Graphic.Width, artEntry.Graphic.Height,
266
        artEntry.Graphic);
267
      FArtCache.StoreID(id, Result);
268
269
      artEntry.Free;
270
    end;
271
  end;
272
end;
273
274
function TLandTextureManager.GetFlatLandMaterial(ATileID: Word): TMaterial;
275
var
276
  artEntry: TArt;
277
begin
278
  if not FFlatLandArtCache.QueryID(ATileID, TObject(Result)) then
279
  begin
280
    artEntry := ResMan.Art.GetFlatLand(ATileID);
281
282
    Result := TMaterial.Create(artEntry.Graphic.Width, artEntry.Graphic.Height,
283
      artEntry.Graphic);
284
    FFlatLandArtCache.StoreID(ATileID, Result);
285
286
    artEntry.Free;
287
  end;
288
end;
289
290
function TLandTextureManager.GetTexMaterial(ATileID: Word): TMaterial;
291
var
292
  texEntry: TTexture;
293
  texID: Integer;
294
begin
295
  if not FTexCache.QueryID(ATileID, TObject(Result)) then
296
  begin
297
    texID := ResMan.Tiledata.LandTiles[ATileID].TextureID;
298
    if texID > 0 then
299
    begin
300
      texEntry := TTexture(ResMan.Texmaps.Block[texID]);
301
302
      Result := TMaterial.Create(texEntry.Graphic.Width, texEntry.Graphic.Height,
303
        texEntry.Graphic);
304
      FTexCache.StoreID(ATileID, Result);
305
306
      texEntry.Free;
307
    end else
308
      Result := nil;
309
  end;
310
end;
311
312
{ TBlock }
313
314
constructor TBlock.Create(AMap: TMapBlock; AStatics: TStaticBlock);
315
begin
316
  inherited Create;
317
  FMapBlock := AMap;
318
  FStaticBlock := AStatics;
319
end;
320
321
destructor TBlock.Destroy;
322
begin
323
  if FMapBlock <> nil then FreeAndNil(FMapBlock);
324
  if FStaticBlock <> nil then FreeAndNil(FStaticBlock);
325
  inherited Destroy;
326
end;
327
328
{ TLandscape }
329
330
constructor TLandscape.Create(AWidth, AHeight: Word);
331
var
332
  blockID: Integer;
333
begin
334
  inherited Create;
335
  FWidth := AWidth;
336
  FHeight := AHeight;
337
  FCellWidth := FWidth * 8;
338
  FCellHeight := FHeight * 8;
339
  FBlockCache := TCacheManager.Create(256);
340
  FBlockCache.OnRemoveObject := @OnRemoveCachedObject;
341
  
342
  SetLength(FOpenRequests, FWidth * FHeight);
343
  for blockID := 0 to Length(FOpenRequests) - 1 do
344
    FOpenRequests[blockID] := False;
345
  
346
  RegisterPacketHandler($04, TPacketHandler.Create(0, @OnBlocksPacket));
347
  RegisterPacketHandler($06, TPacketHandler.Create(8, @OnDrawMapPacket));
348
  RegisterPacketHandler($07, TPacketHandler.Create(10, @OnInsertStaticPacket));
349
  RegisterPacketHandler($08, TPacketHandler.Create(10, @OnDeleteStaticPacket));
350
  RegisterPacketHandler($09, TPacketHandler.Create(11, @OnElevateStaticPacket));
351
  RegisterPacketHandler($0A, TPacketHandler.Create(14, @OnMoveStaticPacket));
352
  RegisterPacketHandler($0B, TPacketHandler.Create(12, @OnHueStaticPacket));
353
end;
354
355
destructor TLandscape.Destroy;
356
var
357
  i: Integer;
358
begin
359
  if FBlockCache <> nil then
360
  begin
361
    FBlockCache.OnRemoveObject := nil;
362
    FreeAndNil(FBlockCache);
363
  end;
364
  
365
  RegisterPacketHandler($04, nil);
366
  RegisterPacketHandler($06, nil);
367
  RegisterPacketHandler($07, nil);
368
  RegisterPacketHandler($08, nil);
369
  RegisterPacketHandler($09, nil);
370
  RegisterPacketHandler($0A, nil);
371
  RegisterPacketHandler($0B, nil);
372
  
373
  inherited Destroy;
374
end;
375
376
function TLandscape.GetMapCell(AX, AY: Word): TMapCell;
377
var
378
  block: TMapBlock;
379
begin
380
  Result := nil;
381
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
382
  begin
383
    block := GetMapBlock(AX div 8, AY div 8);
384
    if block <> nil then
385
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
386
  end;
387
end;
388
389
function TLandscape.GetLandAlt(AX, AY: Word; ADefault: ShortInt): ShortInt;
390
var
391
  cell: TMapCell;
392
begin
393
  cell := MapCell[AX, AY];
394
  if cell <> nil then
395
    Result := cell.Altitude
396
  else
397
    Result := ADefault;
398
end;
399
400
function TLandscape.GetStaticList(AX, AY: Word): TList;
401
var
402
  block: TSeperatedStaticBlock;
403
begin
404
  Result := nil;
405
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
406
  begin
407
    block := GetStaticBlock(AX div 8, AY div 8);
408
    if block <> nil then
409
      Result := block.Cells[(AY mod 8) * 8 + AX mod 8];
410
  end;
411
end;
412
413
function TLandscape.Compare(left, right: TObject): Integer;
414
begin
415
  Result := TWorldItem(right).Priority - TWorldItem(left).Priority;
416
  if Result = 0 then
417
  begin
418
    if (left is TMapCell) and (right is TStaticItem) then
419
      Result := 1
420
    else if (left is TStaticItem) and (right is TMapCell) then
421
      Result := -1;
422
  end;
423
424
  if Result = 0 then
425
    Result := TWorldItem(right).PriorityBonus - TWorldItem(left).PriorityBonus;
426
427
  if Result = 0 then
428
    Result := TWorldItem(right).PrioritySolver - TWorldItem(left).PrioritySolver;
429
end;
430
431
function TLandscape.GetDrawList(AX, AY: Word; AMinZ, AMaxZ: ShortInt;
432
  AGhostTile: TWorldItem; AVirtualLayer: TStaticItem; AMap,
433
  AStatics: Boolean; ANoDraw:Boolean; AStaticsFilter: TStaticFilter): TList;
434
var
435
  landAlt: ShortInt;
436
  drawMapCell: TMapCell;
437
  drawStatics: TList;
438
  i: Integer;
439
begin
440
  Result := TList.Create;
441
  if AMap then
442
  begin
443
    landAlt := GetLandAlt(AX, AY, 0);
444
    if (landAlt >= AMinZ) and (landAlt <= AMaxZ) then
445
    begin
446
      drawMapCell := GetMapCell(AX, AY);
447
      if (drawMapCell <> nil) and (ANoDraw or (drawMapCell.TileID > 2)) then
448
      begin
449
        drawMapCell.Priority := GetEffectiveAltitude(drawMapCell);
450
        drawMapCell.PriorityBonus := 0;
451
        drawMapCell.PrioritySolver := 0;
452
        Result.Add(drawMapCell);
453
      end;
454
      
455
      if AGhostTile is TMapCell then
456
      begin
457
        AGhostTile.X := AX;
458
        AGhostTile.Y := AY;
459
        AGhostTile.Priority := GetEffectiveAltitude(TMapCell(AGhostTile));
460
        AGhostTile.PriorityBonus := 0;
461
        AGhostTile.PrioritySolver := 0;
462
        Result.Add(AGhostTile);
463
      end;
464
    end;
465
  end;
466
467
  if AStatics then
468
  begin
469
    drawStatics := GetStaticList(AX, AY);
470
    if drawStatics <> nil then
471
      for i := 0 to drawStatics.Count - 1 do
472
        if (TStaticItem(drawStatics[i]).Z >= AMinZ) and
473
           (TStaticItem(drawStatics[i]).Z <= AMaxZ) and
474
           ((AStaticsFilter = nil) or AStaticsFilter(TStaticItem(drawStatics[i]))) then
475
        begin
476
          UpdateStaticsPriority(TStaticItem(drawStatics[i]), i + 1);
477
          Result.Add(Pointer(drawStatics[i]));
478
        end;
479
        
480
481
    if AGhostTile is TStaticItem then
482
    begin
483
      UpdateStaticsPriority(TStaticItem(AGhostTile), MaxInt);
484
      Result.Add(AGhostTile);
485
    end;
486
  end;
487
  
488
  if AVirtualLayer <> nil then
489
  begin
490
    UpdateStaticsPriority(AVirtualLayer, MaxInt-1);
491
    Result.Add(AVirtualLayer);
492
  end;
493
  
494
  ListSort(Result, @Compare);
495
end;
496
497
function TLandscape.GetEffectiveAltitude(ATile: TMapCell): ShortInt;
498
var
499
  north, west, south, east: ShortInt;
500
begin
501
  north := ATile.Altitude;
502
  west := GetLandAlt(ATile.X, ATile.Y + 1, north);
503
  south := GetLandAlt(ATile.X + 1, ATile.Y + 1, north);
504
  east := GetLandAlt(ATile.X + 1, ATile.Y, north);
505
506
  if Abs(north - south) > Abs(west - east) then
507
    Result := (north + south) div 2
508
  else
509
    Result := (west + east) div 2;
510
end;
511
512
function TLandscape.GetNormals(AX, AY: Word): TNormals;
513
var
514
  cells: array[0..2, 0..2] of TNormals;
515
  north, west, south, east: TVector;
516
  i, j: Integer;
517
518
  function GetPlainNormals(X, Y: SmallInt): TNormals;
519
  var
520
    cell: TMapCell;
521
    north, west, south, east: ShortInt;
522
    u, v: TVector;
523
  begin
524
    cell := GetMapCell(X, Y);
525
    if Assigned(cell) then
526
    begin
527
      north := cell.Altitude;
528
      west := GetLandAlt(cell.X, cell.Y + 1, north);
529
      south := GetLandAlt(cell.X + 1, cell.Y + 1, north);
530
      east := GetLandAlt(cell.X + 1, cell.Y, north);
531
    end else
532
    begin
533
      north := 0;
534
      west := 0;
535
      east := 0;
536
      south := 0;
537
    end;
538
539
    if (north = west) and (west = east) and (north = south) then
540
    begin
541
      Result[0] := Vector(0, 0, 1);
542
      Result[1] := Vector(0, 0, 1);
543
      Result[2] := Vector(0, 0, 1);
544
      Result[3] := Vector(0, 0, 1);
545
    end else
546
    begin
547
      u := Vector(-22, 22, (north - east) * 4);
548
      v := Vector(-22, -22, (west - north) * 4);
549
      Result[0] := VectorNorm(VectorCross(u, v));
550
551
      u := Vector(22, 22, (east - south) * 4);
552
      v := Vector(-22, 22, (north - east) * 4);
553
      Result[1] := VectorNorm(VectorCross(u, v));
554
555
      u := Vector(22, -22, (south - west) * 4);
556
      v := Vector(22, 22, (east - south) * 4);
557
      Result[2] := VectorNorm(VectorCross(u, v));
558
559
      u := Vector(-22, -22, (west - north) * 4);
560
      v := Vector(22, -22, (south - west) * 4);
561
      Result[3] := VectorNorm(VectorCross(u, v));
562
    end;
563
  end;
564
begin
565
  for i := 0 to 2 do
566
    for j := 0 to 2 do
567
      cells[i, j] := GetPlainNormals(AX - 1 + i, AY - 1 + j);
568
569
  north := cells[0, 0][2];
570
  west := cells[0, 1][1];
571
  east := cells[1, 0][3];
572
  south := cells[1, 1][0];
573
  Result[0] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
574
575
  north := cells[1, 0][2];
576
  west := cells[1, 1][1];
577
  east := cells[2, 0][3];
578
  south := cells[2, 1][0];
579
  Result[1] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
580
581
  north := cells[1, 1][2];
582
  west := cells[1, 2][1];
583
  east := cells[2, 1][3];
584
  south := cells[2, 2][0];
585
  Result[2] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
586
587
  north := cells[0, 1][2];
588
  west := cells[0, 2][1];
589
  east := cells[1, 1][3];
590
  south := cells[1, 2][0];
591
  Result[3] := VectorNorm(VectorAdd(VectorAdd(VectorAdd(north, west), east), south));
592
end;
593
594
procedure TLandscape.OnBlockChanged(ABlock: TMulBlock);
595
var
596
  block, old: TWorldBlock;
597
  mode: Byte;
598
  id, blockID: Integer;
599
begin
600
  {block := ABlock as TWorldBlock;
601
  if block <> nil then
602
  begin
603
    if block is TSeperatedStaticBlock then
604
      mode := mStatics
605
    else
606
      mode := mMap;
607
    id := GetID(block.X, block.Y, mode);
608
    blockID := (block.X * FHeight) + block.Y;
609
    if block.Changed or (block.RefCount > 0) then
610
    begin
611
      if FPersistentBlocks[blockID][mode] = nil then
612
      begin
613
        FPersistentBlocks[blockID][mode] := block;
614
        FBlockCache.DiscardID(id);
615
      end;
616
    end else
617
    begin
618
      FPersistentBlocks[blockID][mode] := nil;
619
      if not FBlockCache.QueryID(id, TObject(old)) then
620
        FBlockCache.StoreID(id, block);
621
    end;
622
  end;}
623
end;
624
625
procedure TLandscape.MoveStatic(AStatic: TStaticItem; AX, AY: Word);
626
var
627
  sourceBlock, targetBlock: TSeperatedStaticBlock;
628
  targetStaticList: TList;
629
  i: Integer;
630
begin
631
  if (AX >= 0) and (AX <= FCellWidth) and (AY >= 0) and (AY <= FCellHeight) then
632
  begin
633
    sourceBlock := AStatic.Owner as TSeperatedStaticBlock;
634
    targetBlock := GetStaticBlock(AX div 8, AY div 8);
635
    if (sourceBlock <> nil) and (targetBlock <> nil) then
636
    begin
637
      sourceBlock.Cells[(AStatic.Y mod 8) * 8 + AStatic.X mod 8].Remove(AStatic);
638
      targetStaticList := targetBlock.Cells[(AY mod 8) * 8 + AX mod 8];
639
      targetStaticList.Add(AStatic);
640
      for i := 0 to targetStaticList.Count - 1 do
641
        UpdateStaticsPriority(TStaticItem(targetStaticList.Items[i]), i);
642
      ListSort(targetStaticList, @Compare);
643
      AStatic.UpdatePos(AX, AY, AStatic.Z);
644
      AStatic.Owner := targetBlock;
645
    end;
646
  end;
647
end;
648
649
procedure TLandscape.PrepareBlocks(AX1, AY1, AX2, AY2: Word);
650
var
651
  x, y, i, mapID, staticID: Integer;
652
  coords: TBlockCoordsArray;
653
  obj: TObject;
654
begin
655
  AX1 := EnsureRange(AX1, 0, FWidth - 1);
656
  AY1 := EnsureRange(AY1, 0, FHeight - 1);
657
  AX2 := EnsureRange(AX2, 0, FWidth - 1);
658
  AY2 := EnsureRange(AY2, 0, FHeight - 1);
659
660
  SetLength(coords, 0);
661
  for x := AX1 to AX2 do
662
  begin
663
    for y := AY1 to AY2 do
664
    begin
665
      if (not FOpenRequests[y * FWidth + x]) and
666
         (not FBlockCache.QueryID(GetID(x, y), obj)) then
667
      begin
668
        SetLength(coords, Length(coords) + 1);
669
        i := High(coords);
670
        coords[i].X := x;
671
        coords[i].Y := y;
672
        FOpenRequests[y * FWidth + x] := True;
673
      end;
674
    end;
675
  end;
676
  if Length(coords) > 0 then
677
    dmNetwork.Send(TRequestBlocksPacket.Create(coords));
678
end;
679
680
function TLandscape.GetMapBlock(AX, AY: Word): TMapBlock;
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), TObject(block)) then
688
      Result := block.Map;
689
  end;
690
end;
691
692
function TLandscape.GetStaticBlock(AX, AY: Word): TSeperatedStaticBlock;
693
var
694
  block: TBlock;
695
begin
696
  Result := nil;
697
  if (AX >= 0) and (AX < FWidth) and (AY >= 0) and (AY < FHeight) then
698
  begin
699
    if FBlockCache.QueryID(GetID(AX, AY), TObject(block)) then
700
      Result := TSeperatedStaticBlock(block.Static);
701
  end;
702
end;
703
704
procedure TLandscape.UpdateStaticsPriority(AStaticItem: TStaticItem;
705
  APrioritySolver: Integer);
706
var
707
  staticTileData: TStaticTileData;
708
begin
709
  staticTileData := ResMan.Tiledata.StaticTiles[AStaticItem.TileID];
710
  AStaticItem.PriorityBonus := 0;
711
  if not ((staticTileData.Flags and tdfBackground) = tdfBackground) then
712
    AStaticItem.PriorityBonus := AStaticItem.PriorityBonus + 1;
713
  if staticTileData.Height > 0 then
714
    AStaticItem.PriorityBonus := AStaticItem.PriorityBonus + 1;
715
  AStaticItem.Priority := AStaticItem.Z + AStaticItem.PriorityBonus;
716
  AStaticItem.PrioritySolver := APrioritySolver;
717
end;
718
719
procedure TLandscape.OnBlocksPacket(ABuffer: TEnhancedMemoryStream);
720
var
721
  index: TGenericIndex;
722
  map: TMapBlock;
723
  statics: TStaticBlock;
724
  coords: TBlockCoords;
725
  count: Word;
726
  id: Integer;
727
begin
728
  index := TGenericIndex.Create(nil);
729
  while ABuffer.Position < ABuffer.Size do
730
  begin
731
    ABuffer.Read(coords, SizeOf(TBlockCoords));
732
    id := GetID(coords.X, coords.Y);
733
  
734
    map := TMapBlock.Create(ABuffer, coords.X, coords.Y);
735
    count := ABuffer.ReadWord;
736
    if count > 0 then
737
      index.Lookup := ABuffer.Position
738
    else
739
      index.Lookup := $FFFFFFFF;
740
    index.Size := count * 7;
741
    statics := TSeperatedStaticBlock.Create(ABuffer, index, coords.X, coords.Y);
742
743
    FBlockCache.RemoveID(id);
744
    FBlockCache.StoreID(id, TBlock.Create(map, statics));
745
    
746
    FOpenRequests[coords.Y * FWidth + coords.X] := False;
747
  end;
748
  index.Free;
749
  if Assigned(FOnChange) then FOnChange;
750
end;
751
752
procedure TLandscape.OnDrawMapPacket(ABuffer: TEnhancedMemoryStream);
753
var
754
  x, y: Word;
755
  cell: TMapCell;
756
begin
757
  x := ABuffer.ReadWord;
758
  y := ABuffer.ReadWord;
759
  cell := GetMapCell(x, y);
760
  if cell <> nil then
761
  begin
762
    cell.Altitude := ABuffer.ReadShortInt;
763
    cell.TileID := ABuffer.ReadWord;
764
    if Assigned(FOnChange) then FOnChange;
765
  end;
766
end;
767
768
procedure TLandscape.OnInsertStaticPacket(ABuffer: TEnhancedMemoryStream);
769
var
770
  x, y: Word;
771
  block: TSeperatedStaticBlock;
772
  staticItem: TStaticItem;
773
  targetStaticList: TList;
774
  i: Integer;
775
begin
776
  x := ABuffer.ReadWord;
777
  y := ABuffer.ReadWord;
778
  block := GetStaticBlock(x div 8, y div 8);
779
  if block <> nil then
780
  begin
781
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
782
    staticItem.X := x;
783
    staticItem.Y := y;
784
    staticItem.Z := ABuffer.ReadShortInt;
785
    staticItem.TileID := ABuffer.ReadWord;
786
    staticItem.Hue := ABuffer.ReadWord;
787
    targetStaticList := block.Cells[(y mod 8) * 8 + x mod 8];
788
    targetStaticList.Add(staticItem);
789
    for i := 0 to targetStaticList.Count - 1 do
790
      UpdateStaticsPriority(TStaticItem(targetStaticList.Items[i]), i);
791
    ListSort(targetStaticList, @Compare);
792
    staticItem.Owner := block;
793
    if Assigned(FOnChange) then FOnChange;
794
  end;
795
end;
796
797
procedure TLandscape.OnDeleteStaticPacket(ABuffer: TEnhancedMemoryStream);
798
var
799
  block: TSeperatedStaticBlock;
800
  i: Integer;
801
  statics: TList;
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 := TStaticItem(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
        statics.Delete(i);
818
        staticItem.Delete;
819
        if Assigned(FOnChange) then FOnChange;
820
        Break;
821
      end;
822
    end;
823
  end;
824
end;
825
826
procedure TLandscape.OnElevateStaticPacket(ABuffer: TEnhancedMemoryStream);
827
var
828
  block: TSeperatedStaticBlock;
829
  i,j : Integer;
830
  statics: TList;
831
  staticInfo: TStaticInfo;
832
  staticItem: TStaticItem;
833
begin
834
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
835
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
836
  if block <> nil then
837
  begin
838
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
839
    for i := 0 to statics.Count - 1 do
840
    begin
841
      staticItem := TStaticItem(statics.Items[i]);
842
      if (staticItem.Z = staticInfo.Z) and
843
         (staticItem.TileID = staticInfo.TileID) and
844
         (staticItem.Hue = staticInfo.Hue) then
845
      begin
846
        staticItem.Z := ABuffer.ReadShortInt;
847
        for j := 0 to statics.Count - 1 do
848
          UpdateStaticsPriority(TStaticItem(statics.Items[j]), j);
849
        ListSort(statics, @Compare);
850
        if Assigned(FOnChange) then FOnChange;
851
        Break;
852
      end;
853
    end;
854
  end;
855
end;
856
857
procedure TLandscape.OnMoveStaticPacket(ABuffer: TEnhancedMemoryStream);
858
var
859
  sourceBlock, targetBlock: TSeperatedStaticBlock;
860
  i: Integer;
861
  statics: TList;
862
  staticInfo: TStaticInfo;
863
  staticItem: TStaticItem;
864
  newX, newY: Word;
865
  item: PLinkedItem;
866
begin
867
  staticItem := nil;
868
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
869
  newX := EnsureRange(ABuffer.ReadWord, 0, FCellWidth - 1);
870
  newY := EnsureRange(ABuffer.ReadWord, 0, FCellHeight - 1);
871
872
  sourceBlock := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
873
  targetBlock := GetStaticBlock(newX div 8, newY div 8);
874
  if sourceBlock <> nil then
875
  begin
876
    statics := sourceBlock.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
877
    i := 0;
878
    while (i < statics.Count) and (staticItem = nil) do
879
    begin
880
      staticItem := TStaticItem(statics.Items[i]);
881
      if (staticItem.Z <> staticInfo.Z) or
882
         (staticItem.TileID <> staticInfo.TileID) or
883
         (staticItem.Hue <> staticInfo.Hue) then
884
      begin
885
        staticItem := nil;
886
      end;
887
      Inc(i);
888
    end;
889
890
    if staticItem <> nil then
891
    begin
892
      statics.Remove(staticItem);
893
      staticItem.Delete;
894
    end;
895
  end;
896
  
897
  if targetBlock <> nil then
898
  begin
899
    staticItem := TStaticItem.Create(nil, nil, 0, 0);
900
    staticItem.X := newX;
901
    staticItem.Y := newY;
902
    staticItem.Z := staticInfo.Z;
903
    staticItem.TileID := staticInfo.TileID;
904
    staticItem.Hue := staticInfo.Hue;
905
    statics := targetBlock.Cells[(newY mod 8) * 8 + newX mod 8];
906
    statics.Add(staticItem);
907
    for i := 0 to statics.Count - 1 do
908
      UpdateStaticsPriority(TStaticItem(statics.Items[i]), i);
909
    ListSort(statics, @Compare);
910
    staticItem.Owner := targetBlock;
911
  end;
912
  
913
  if Assigned(FOnChange) then FOnChange;
914
end;
915
916
procedure TLandscape.OnHueStaticPacket(ABuffer: TEnhancedMemoryStream);
917
var
918
  block: TSeperatedStaticBlock;
919
  i,j : Integer;
920
  statics: TList;
921
  staticInfo: TStaticInfo;
922
  staticItem: TStaticItem;
923
begin
924
  ABuffer.Read(staticInfo, SizeOf(TStaticInfo));
925
  block := GetStaticBlock(staticInfo.X div 8, staticInfo.Y div 8);
926
  if block <> nil then
927
  begin
928
    statics := block.Cells[(staticInfo.Y mod 8) * 8 + staticInfo.X mod 8];
929
    for i := 0 to statics.Count - 1 do
930
    begin
931
      staticItem := TStaticItem(statics.Items[i]);
932
      if (staticItem.Z = staticInfo.Z) and
933
         (staticItem.TileID = staticInfo.TileID) and
934
         (staticItem.Hue = staticInfo.Hue) then
935
      begin
936
        staticItem.Hue := ABuffer.ReadWord;
937
        if Assigned(FOnChange) then FOnChange;
938
        Break;
939
      end;
940
    end;
941
  end;
942
end;
943
944
procedure TLandscape.OnRemoveCachedObject(AObject: TObject);
945
var
946
  block: TBlock;
947
begin
948
  block := AObject as TBlock;
949
  if block <> nil then
950
    dmNetwork.Send(TFreeBlockPacket.Create(block.Map.X, block.Map.Y));
951
end;
952
953
{ TMaterial }
954
955
constructor TMaterial.Create(AWidth, AHeight: Integer;
956
  AGraphic: TSingleImage);
957
var
958
  caps: TGLTextureCaps;
959
begin
960
  inherited Create;
961
  FRealWidth := AWidth;
962
  FRealHeight := AHeight;
963
  GetGLTextureCaps(caps);
964
  if caps.PowerOfTwo then
965
  begin
966
    if IsPow2(AWidth) then FWidth := AWidth else FWidth := NextPow2(AWidth);
967
    if IsPow2(AHeight) then FHeight := AHeight else FHeight := NextPow2(AHeight);
968
  end else
969
  begin
970
    FWidth := AWidth;
971
    FHeight := AHeight;
972
  end;
973
  FGraphic := TSingleImage.CreateFromParams(FWidth, FHeight, ifA8R8G8B8);
974
  AGraphic.CopyTo(0, 0, AWidth, AHeight, FGraphic, 0, 0);
975
  UpdateTexture;
976
end;
977
978
destructor TMaterial.Destroy;
979
begin
980
  if FGraphic <> nil then FreeAndNil(FGraphic);
981
  if FTexture <> 0 then glDeleteTextures(1, @FTexture);
982
  inherited Destroy;
983
end;
984
985
function TMaterial.HitTest(AX, AY: Integer): Boolean;
986
var
987
  pixel: TColor32Rec;
988
begin
989
  Result := False;
990
  //writeln(FGraphic.Width, ',', FGraphic.Height, ',', AX, ',', AY);
991
  if InRange(AX, 0, FGraphic.Width - 1) and
992
     InRange(AY, 0, FGraphic.Height - 1) then
993
  begin
994
    pixel := GetPixel32(FGraphic.ImageDataPointer^, AX, AY);
995
    if pixel.A > 0 then
996
      Result := True;
997
  end;
998
end;
999
1000
procedure TMaterial.UpdateTexture;
1001
begin
1002
  if FTexture <> 0 then glDeleteTextures(1, @FTexture);
1003
1004
  FTexture := CreateGLTextureFromImage(FGraphic.ImageDataPointer^, 0, 0, False, ifUnknown, @FWidth, @FHeight);
1005
  glBindTexture(GL_TEXTURE_2D, FTexture);
1006
  {glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, @FWidth);
1007
  glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, @FHeight);}
1008
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1009
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1010
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1011
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1012
end;
1013
1014
{ TTileList }
1015
1016
constructor TTileList.Create;
1017
begin
1018
  inherited Create;
1019
  FFirst := nil;
1020
  FLastBlock := nil;
1021
end;
1022
1023
destructor TTileList.Destroy;
1024
begin
1025
  Clear;
1026
  inherited Destroy;
1027
end;
1028
1029
procedure TTileList.Clear;
1030
var
1031
  current, next: PBlockInfo;
1032
begin
1033
  current := FFirst;
1034
  while current <> nil do
1035
  begin
1036
    next := current^.Next;
1037
    Dispose(current);
1038
    current := next;
1039
  end;
1040
  FFirst := nil;
1041
  FLastBlock := nil;
1042
end;
1043
1044
function TTileList.Iterate(var ABlockInfo: PBlockInfo): Boolean;
1045
begin
1046
  if ABlockInfo = nil then
1047
    ABlockInfo := FFirst
1048
  else
1049
    ABlockInfo := ABlockInfo^.Next;
1050
  Result := ABlockInfo <> nil;
1051
end;
1052
1053
procedure TTileList.Add(AItem: TWorldItem);
1054
var
1055
  current: PBlockInfo;
1056
begin
1057
  New(current);
1058
  current^.Item := AItem;
1059
  current^.Next := nil;
1060
  if FFirst = nil then FFirst := current;
1061
  if FLastBlock <> nil then FLastBlock^.Next := current;
1062
  FLastBlock := current;
1063
end;
1064
1065
procedure TTileList.Delete(AItem: TWorldItem);
1066
var
1067
  current, last, next: PBlockInfo;
1068
begin
1069
  last := nil;
1070
  current := FFirst;
1071
  while current <> nil do
1072
  begin
1073
    if current^.Item = AItem then
1074
    begin
1075
      if FFirst = current then FFirst := current^.Next;
1076
      if FLastBlock = current then FLastBlock := last;
1077
      if last <> nil then last^.Next := current^.Next;
1078
      Dispose(current);
1079
      next := nil;
1080
    end else
1081
      next := current^.Next;
1082
    last := current;
1083
    current := next;
1084
  end;
1085
end;
1086
1087
{ TScreenBuffer }
1088
1089
procedure TScreenBuffer.OnTileRemoved(ATile: TMulBlock);
1090
var
1091
  currentItem, lastItem, nextItem: PBlockInfo;
1092
begin
1093
  lastItem := nil;
1094
  currentItem := FFirst;
1095
  while currentItem <> nil do
1096
  begin
1097
    if currentItem^.Item = ATile then
1098
    begin
1099
      if FFirst = currentItem then FFirst := currentItem^.Next;
1100
      if FLastBlock = currentItem then FLastBlock := lastItem;
1101
      if lastItem <> nil then lastItem^.Next := currentItem^.Next;
1102
      Dispose(currentItem);
1103
      nextItem := nil;
1104
    end else
1105
      nextItem := currentItem^.Next;
1106
    lastItem := currentItem;
1107
    currentItem := nextItem;
1108
  end;
1109
end;
1110
1111
procedure TScreenBuffer.Clear;
1112
var
1113
  current, next: PBlockInfo;
1114
begin
1115
  current := FFirst;
1116
  while current <> nil do
1117
  begin
1118
    next := current^.Next;
1119
    current^.Item.Locked := False;
1120
    current^.Item.OnDestroy.UnregisterEvent(@OnTileRemoved);
1121
    Dispose(current);
1122
    current := next;
1123
  end;
1124
  FFirst := nil;
1125
  FLastBlock := nil;
1126
end;
1127
1128
function TScreenBuffer.Find(AScreenPosition: TPoint): PBlockInfo;
1129
var
1130
  current: PBlockInfo;
1131
begin
1132
  Result := nil;
1133
  current := FFirst;
1134
  while (current <> nil) and (Result = nil) do
1135
  begin
1136
    if PtInRect(current^.ScreenRect, AScreenPosition) and
1137
       current^.Material.HitTest(AScreenPosition.x - current^.ScreenRect.Left,
1138
                                 AScreenPosition.y - current^.ScreenRect.Top) then
1139
    begin
1140
      Result := current;
1141
    end;
1142
    current := current^.Next;
1143
  end;
1144
end;
1145
1146
procedure TScreenBuffer.Store(AScreenRect: TRect; AItem: TWorldItem;
1147
  AMaterial: TMaterial);
1148
var
1149
  current: PBlockInfo;
1150
begin
1151
  New(current);
1152
  AItem.Locked := True;
1153
  AItem.OnDestroy.RegisterEvent(@OnTileRemoved);
1154
  current^.ScreenRect := AScreenRect;
1155
  current^.Item := AItem;
1156
  current^.Material := AMaterial;
1157
  current^.Next := FFirst;
1158
  FFirst := current;
1159
  if FLastBlock = nil then FLastBlock := current;
1160
end;
1161
1162
end.
1163