source: tags/1.3.0/UMap.pas

Last change on this file was 259, checked in by chronos, 6 years ago

Merged revision(s) 258 from trunk:

  • Fixed: Bad cell links in Voronoi map type causing problems on openning saved file.
  • Added: More checks for loaded game file.
File size: 28.7 KB
Line 
1unit UMap;
2
3{$mode delphi}
4
5interface
6
7uses
8 Classes, SysUtils, Graphics, ExtCtrls, UGeometry, DOM, fgl,
9 UXMLUtils;
10
11const
12 DefaultMaxPower = 99;
13
14type
15 TMapArea = class;
16 TMap = class;
17 TCells = class;
18 TCellLinks = class;
19
20 TTerrainType = (ttVoid, ttNormal, ttCity);
21 TExtraType = (etNone, etObjectiveTarget, etAttack, etDefense, etLookout,
22 etGrowLow, etGrowMedium, etGrowHigh);
23
24 { TCell }
25
26 TCell = class
27 private
28 FArea: TMapArea;
29 FId: Integer;
30 FMap: TMap;
31 FPower: Integer;
32 procedure SetArea(AValue: TMapArea);
33 procedure SetId(AValue: Integer);
34 procedure SetPower(AValue: Integer);
35 public
36 PosPx: TPoint;
37 Polygon: TPolygon;
38 Terrain: TTerrainType;
39 PlayerId: Integer;
40 Player: TObject; // TPlayer;
41 NeighborsId: array of Integer;
42 Neighbors: TCells;
43 Mark: Boolean; // Temporary value
44 Weight: Integer; // Temporary value
45 Angle: Double; // Temporary value
46 PlayerCell: Pointer; // Temporary value
47 Links: TCellLinks;
48 Extra: TExtraType;
49 property Id: Integer read FId write SetId;
50 procedure ConnectTo(Cell: TCell);
51 procedure DisconnectFrom(Cell: TCell);
52 function NeighboringToVoid: Boolean;
53 procedure AreaExtend;
54 procedure FixRefId;
55 procedure LoadFromNode(Node: TDOMNode);
56 procedure SaveToNode(Node: TDOMNode);
57 procedure Assign(Source: TCell);
58 function GetColor: TColor;
59 function ToString: ansistring; override;
60 constructor Create;
61 destructor Destroy; override;
62 property Power: Integer read FPower write SetPower;
63 property Map: TMap read FMap write FMap;
64 property Area: TMapArea read FArea write SetArea;
65 end;
66
67 TCellArray = array of TCell;
68
69 { TCells }
70
71 TCells = class(TFPGObjectList<TCell>)
72 Map: TMap;
73 procedure FixRefId;
74 function FindById(Id: Integer): TCell;
75 procedure GetCellsWithWeight(List: TCells; Low, High: Integer);
76 procedure GetCellsWithExtra(List: TCells; Extra: TExtraType);
77 procedure LoadFromNode(Node: TDOMNode);
78 procedure SaveToNode(Node: TDOMNode);
79 procedure ClearMark;
80 procedure ClearWeight;
81 function ToString: ansistring; override;
82 end;
83
84 { TCellLink }
85
86 TCellLink = class
87 Points: array of TPoint;
88 Cells: TCells;
89 Map: TMap;
90 procedure LoadFromNode(Node: TDOMNode);
91 procedure SaveToNode(Node: TDOMNode);
92 constructor Create;
93 destructor Destroy; override;
94 end;
95
96 { TCellLinks }
97
98 TCellLinks = class(TFPGObjectList<TCellLink>)
99 Map: TMap;
100 function FindByCells(Cell1, Cell2: TCell): TCellLink;
101 function AddLink(Cell1, Cell2: TCell): TCellLink;
102 procedure LoadFromNode(Node: TDOMNode);
103 procedure SaveToNode(Node: TDOMNode);
104 end;
105
106 { TCellLinkParams }
107
108 TCellLinkParams = class
109 Cell1: TCell;
110 Cell2: TCell;
111 Distance: Double;
112 Angle: Double;
113 end;
114
115 TMapShape = (msRectangle, msImage, msRounded);
116
117 { TMapArea }
118
119 TMapArea = class
120 Id: Integer;
121 Map: TMap;
122 BridgeCount: Integer;
123 Cells: TCells;
124 procedure GetBorderCells(List: TCells);
125 constructor Create;
126 destructor Destroy; override;
127 end;
128
129 TMapAreas = class(TFPGObjectList<TMapArea>)
130 end;
131
132 { TMap }
133
134 TMap = class
135 private
136 function GetPixelRect: TRect;
137 function GetSize: TPoint; virtual;
138 function SearchDifferentCellArea(List: TCells; SourceArea,
139 DestArea: TMapArea): TCell;
140 protected
141 FSize: TPoint;
142 FPixelRect: TRect;
143 FNewCellId: Integer;
144 function GetNewCellId: Integer; virtual;
145 procedure SortNeighborsByAngle;
146 procedure SetSize(AValue: TPoint); virtual;
147 public
148 Game: TObject; //TGame;
149 MaxPower: Integer;
150 DefaultCellSize: TPoint;
151 Cells: TCells;
152 Shape: TMapShape;
153 Image: TImage;
154 CellLinks: TCellLinks;
155 Areas: TMapAreas;
156 Cyclic: Boolean;
157 function IsOutsideShape(Coord: TPoint): Boolean; virtual;
158 function IsCellsNeighbor(Cell1, Cell2: TCell): Boolean; virtual;
159 function IsValidIndex(Index: TPoint): Boolean; virtual;
160 procedure Assign(Source: TMap); virtual;
161 procedure LoadFromFile(FileName: string); virtual;
162 procedure SaveToFile(FileName: string); virtual;
163 procedure LoadFromNode(Node: TDOMNode);
164 procedure SaveToNode(Node: TDOMNode);
165 function PosToCell(Pos: TPoint): TCell; virtual;
166 function CellToPos(Cell: TCell): TPoint; virtual;
167 procedure ComputePlayerStats; virtual;
168 procedure Generate; virtual;
169 procedure BuildMapAreas;
170 procedure BuildBridges;
171 procedure MakeSymetric;
172 procedure CreateLinks;
173 procedure Clear;
174 procedure CheckCells;
175 constructor Create; virtual;
176 destructor Destroy; override;
177 function CalculatePixelRect: TRect; virtual;
178 procedure ForEachCells(Method: TMethod); virtual;
179 property Size: TPoint read GetSize write SetSize;
180 property PixelRect: TRect read GetPixelRect;
181 end;
182
183
184resourcestring
185 SCellRemoveNeighborError = 'Can''t remove cell from neighbour cell';
186 SNegativeCellPowerNotAllowed = 'Not allowed to substract power under zero to negative value';
187
188implementation
189
190uses
191 UPlayer, UGame;
192
193{ TCellLink }
194
195procedure TCellLink.LoadFromNode(Node: TDOMNode);
196var
197 Node2: TDOMNode;
198 Node3: TDOMNode;
199begin
200 Node3 := Node.FindNode('Points');
201 if Assigned(Node3) then begin
202 SetLength(Points, 0);
203 Node2 := Node3.FirstChild;
204 while Assigned(Node2) and (Node2.NodeName = 'Point') do begin
205 SetLength(Points, Length(Points) + 1);
206 Points[High(Points)].X := ReadInteger(Node2, 'X', 0);
207 Points[High(Points)].Y := ReadInteger(Node2, 'Y', 0);
208 Node2 := Node2.NextSibling;
209 end;
210 end;
211end;
212
213procedure TCellLink.SaveToNode(Node: TDOMNode);
214var
215 NewNode: TDOMNode;
216 NewNode2: TDOMNode;
217 I: Integer;
218begin
219 NewNode := Node.OwnerDocument.CreateElement('Points');
220 Node.AppendChild(NewNode);
221 for I := 0 to Length(Points) - 1 do begin
222 NewNode2 := NewNode.OwnerDocument.CreateElement('Point');
223 NewNode.AppendChild(NewNode2);
224 WriteInteger(NewNode2, 'X', Points[I].X);
225 WriteInteger(NewNode2, 'Y', Points[I].Y);
226 end;
227end;
228
229constructor TCellLink.Create;
230begin
231 Cells := TCells.Create;
232 Cells.FreeObjects := False;
233end;
234
235destructor TCellLink.Destroy;
236var
237 I: Integer;
238begin
239 for I := 0 to Cells.Count - 1 do begin
240 if Cells[I].Neighbors.Remove(Cells[1 - I]) = -1 then
241 raise Exception.Create(SCellRemoveNeighborError);
242 if Cells[I].Links.Remove(Self) = -1 then
243 raise Exception.Create(SCellRemoveNeighborError);
244 end;
245 FreeAndNil(Cells);
246 inherited Destroy;
247end;
248
249{ TCellLinks }
250
251function TCellLinks.FindByCells(Cell1, Cell2: TCell): TCellLink;
252var
253 I: Integer;
254begin
255 I := 0;
256 while (I < Count) do begin
257 if ((TCellLink(Items[I]).Cells[0] = Cell1) and (TCellLink(Items[I]).Cells[1] = Cell2)) or
258 ((TCellLink(Items[I]).Cells[0] = Cell2) and (TCellLink(Items[I]).Cells[1] = Cell1)) then
259 Break;
260 Inc(I);
261 end;
262 if I < Count then Result := TCellLink(Items[I])
263 else Result := nil;
264end;
265
266function TCellLinks.AddLink(Cell1, Cell2: TCell): TCellLink;
267begin
268 Result := TCellLink.Create;
269 Cell1.ConnectTo(Cell2);
270 Cell1.Links.Add(Result);
271 Cell2.Links.Add(Result);
272 SetLength(Result.Points, 2);
273 Result.Cells.Add(Cell1);
274 Result.Points[0] := Cell1.PosPx;
275 Result.Cells.Add(Cell2);
276 Result.Points[1] := Cell2.PosPx;
277 Result.Map := Map;
278 Map.CellLinks.Add(Result);
279end;
280
281procedure TCellLinks.LoadFromNode(Node: TDOMNode);
282var
283 Node2: TDOMNode;
284 NewCell: TCellLink;
285begin
286 Count := 0;
287 Node2 := Node.FirstChild;
288 while Assigned(Node2) and (Node2.NodeName = 'CellLink') do begin
289 NewCell := TCellLink.Create;
290 //NewCell.Map := Map;
291 NewCell.LoadFromNode(Node2);
292 Add(NewCell);
293 Node2 := Node2.NextSibling;
294 end;
295end;
296
297procedure TCellLinks.SaveToNode(Node: TDOMNode);
298var
299 I: Integer;
300 NewNode2: TDOMNode;
301begin
302 for I := 0 to Count - 1 do
303 with TCellLink(Items[I]) do begin
304 NewNode2 := Node.OwnerDocument.CreateElement('CellLink');
305 Node.AppendChild(NewNode2);
306 SaveToNode(NewNode2);
307 end;
308end;
309
310{ TMapArea }
311
312procedure TMapArea.GetBorderCells(List: TCells);
313var
314 Cell: TCell;
315begin
316 List.Clear;
317 Map.Cells.ClearMark;
318 for Cell in Cells do begin
319 if Cell.NeighboringToVoid and (Cell.Area = Self) and (not Cell.Mark) then begin
320 List.Add(Cell);
321 Cell.Mark := True;
322 end;
323 end;
324end;
325
326constructor TMapArea.Create;
327begin
328 Cells := TCells.Create;
329 Cells.FreeObjects := False;
330end;
331
332destructor TMapArea.Destroy;
333begin
334 FreeAndNil(Cells);
335 inherited Destroy;
336end;
337
338{ TCells }
339
340procedure TCells.FixRefId;
341var
342 I: Integer;
343begin
344 for I := 0 to Count - 1 do
345 Items[I].FixRefId;
346end;
347
348function TCells.FindById(Id: Integer): TCell;
349var
350 I: Integer;
351begin
352 I := 0;
353 while (I < Count) and (Items[I].Id <> Id) do Inc(I);
354 if I < Count then Result := Items[I]
355 else Result := nil;
356end;
357
358procedure TCells.GetCellsWithWeight(List: TCells; Low, High: Integer);
359var
360 Cell: TCell;
361begin
362 List.Clear;
363 for Cell in Self do
364 if (Cell.Terrain <> ttVoid) and (Cell.Weight >= Low) and
365 (Cell.Weight <= High) then List.Add(Cell);
366end;
367
368procedure TCells.GetCellsWithExtra(List: TCells; Extra: TExtraType);
369var
370 Cell: TCell;
371begin
372 List.Clear;
373 for Cell in Self do
374 if Cell.Extra = Extra then List.Add(Cell);
375end;
376
377procedure TCells.LoadFromNode(Node: TDOMNode);
378var
379 Node2: TDOMNode;
380 NewCell: TCell;
381begin
382 Count := 0;
383 Node2 := Node.FirstChild;
384 while Assigned(Node2) and (Node2.NodeName = 'Cell') do begin
385 NewCell := TCell.Create;
386 NewCell.Map := Map;
387 NewCell.LoadFromNode(Node2);
388 Add(NewCell);
389 Node2 := Node2.NextSibling;
390 end;
391end;
392
393procedure TCells.SaveToNode(Node: TDOMNode);
394var
395 I: Integer;
396 NewNode2: TDOMNode;
397begin
398 for I := 0 to Count - 1 do
399 with Items[I] do begin
400 NewNode2 := Node.OwnerDocument.CreateElement('Cell');
401 Node.AppendChild(NewNode2);
402 SaveToNode(NewNode2);
403 end;
404end;
405
406procedure TCells.ClearMark;
407var
408 Cell: TCell;
409begin
410 for Cell in Self do Cell.Mark := False;
411end;
412
413procedure TCells.ClearWeight;
414var
415 Cell: TCell;
416begin
417 for Cell in Self do Cell.Weight := 0;
418end;
419
420function TCells.ToString: ansistring;
421var
422 C: TCell;
423begin
424 Result := '';
425 for C in Self do
426 Result := Result + IntToStr(C.Id) + ', ';
427end;
428
429{ TMap }
430
431function TMap.GetSize: TPoint;
432begin
433 Result:= FSize;
434end;
435
436function TMap.GetPixelRect: TRect;
437begin
438 if FPixelRect.Empty then FPixelRect := CalculatePixelRect;
439 Result := FPixelRect;
440end;
441
442procedure TMap.SetSize(AValue: TPoint);
443begin
444 if (FSize.X <> AValue.X) or (FSize.Y <> AValue.Y) then begin
445 FSize := AValue;
446 end;
447end;
448
449function CompareCellAngle(const C1, C2: TCell): Integer;
450begin
451 if C1.Angle < C2.Angle then Result := -1
452 else if C1.Angle > C2.Angle then Result := 1
453 else Result := 0;
454end;
455
456procedure TMap.SortNeighborsByAngle;
457var
458 Cell: TCell;
459 NeighborCell: TCell;
460begin
461 for Cell in Cells do begin
462 for NeighborCell in Cell.Neighbors do
463 NeighborCell.Angle := TLine.Create(Cell.PosPx, NeighborCell.PosPx).GetAngle;
464
465 Cell.Neighbors.Sort(CompareCellAngle);
466 end;
467end;
468
469function TMap.GetNewCellId: Integer;
470begin
471 Result := FNewCellId;
472 Inc(FNewCellId);
473end;
474
475function TMap.IsOutsideShape(Coord: TPoint): Boolean;
476var
477 Rect: TRect;
478 Color: TColor;
479 Pos: TPoint;
480 Center: TPoint;
481begin
482 case Shape of
483 msRectangle: Result := False;
484 msImage: begin
485 Rect := PixelRect;
486 with Image.Picture.Bitmap do begin
487 Pos := TPoint.Create(Trunc(Coord.X / Rect.Size.X * Width),
488 Trunc(Coord.Y / Rect.Size.Y * Height));
489 Color := Canvas.Pixels[Pos.X, Pos.Y];
490 end;
491 Result := Color <> clWhite;
492 end;
493 msRounded: begin
494 Rect := PixelRect;
495 Center := Rect.Center;
496 Result := Sqr(Coord.X - Center.X) / Sqr(Rect.Size.X div 2) +
497 Sqr(Coord.Y - Center.Y) / Sqr(Rect.Size.Y div 2) > 1;
498 end
499 else Result := False;
500 end;
501end;
502
503function TMap.IsCellsNeighbor(Cell1, Cell2: TCell): Boolean;
504begin
505 Result := Cell1.Neighbors.IndexOf(Cell2) <> -1;
506end;
507
508function TMap.IsValidIndex(Index: TPoint): Boolean;
509begin
510 Result := (Index.X >= 0) and (Index.X < Size.X) and
511 (Index.Y >= 0) and (Index.Y < Size.Y);
512end;
513
514procedure TMap.Assign(Source: TMap);
515//var
516// I: Integer;
517begin
518 // Do not assign Game field
519 MaxPower := Source.MaxPower;
520 Cyclic := Source.Cyclic;
521 Size := Source.Size;
522 DefaultCellSize := Source.DefaultCellSize;
523 Shape := Source.Shape;
524 Image.Picture.Bitmap.Assign(Source.Image.Picture.Bitmap);
525
526 // TODO: How to copy cells
527 {// Copy all cells
528 Cells.Count := 0;
529 Cells.Count := Source.Cells.Count;
530 for I := 0 to Cells.Count - 1 do begin
531 Cells[I] := TCell.Create;
532 Cells[I].Map := Self;
533 Cells[I].Assign(Source.Cells[I]);
534 end;
535 }
536end;
537
538procedure TMap.LoadFromFile(FileName: string);
539begin
540
541end;
542
543procedure TMap.SaveToFile(FileName: string);
544begin
545
546end;
547
548procedure TMap.LoadFromNode(Node: TDOMNode);
549var
550 Node2: TDOMNode;
551begin
552 Size := TPoint.Create(ReadInteger(Node, 'SizeX', 0), ReadInteger(Node, 'SizeY', 0));
553 DefaultCellSize.X := ReadInteger(Node, 'DefaultCellSizeX', 1);
554 DefaultCellSize.Y := ReadInteger(Node, 'DefaultCellSizeY', 1);
555 MaxPower := ReadInteger(Node, 'MaxPower', DefaultMaxPower);
556 Cyclic := ReadBoolean(Node, 'Cyclic', False);
557 Shape := TMapShape(ReadInteger(Node, 'Shape', Integer(msRectangle)));
558 Node2 := Node.FindNode('Cells');
559 if Assigned(Node2) then
560 Cells.LoadFromNode(Node2);
561 Node2 := Node.FindNode('CellLinks');
562 if Assigned(Node2) then
563 CellLinks.LoadFromNode(Node2);
564 FPixelRect := CalculatePixelRect;
565end;
566
567procedure TMap.SaveToNode(Node: TDOMNode);
568var
569 NewNode: TDOMNode;
570begin
571 WriteInteger(Node, 'DefaultCellSizeX', DefaultCellSize.X);
572 WriteInteger(Node, 'DefaultCellSizeY', DefaultCellSize.Y);
573 WriteInteger(Node, 'MaxPower', MaxPower);
574 WriteBoolean(Node, 'Cyclic', Cyclic);
575 WriteInteger(Node, 'Shape', Integer(Shape));
576 WriteInteger(Node, 'SizeX', Size.X);
577 WriteInteger(Node, 'SizeY', Size.Y);
578 NewNode := Node.OwnerDocument.CreateElement('Cells');
579 Node.AppendChild(NewNode);
580 Cells.SaveToNode(NewNode);
581 NewNode := Node.OwnerDocument.CreateElement('CellLinks');
582 Node.AppendChild(NewNode);
583 CellLinks.SaveToNode(NewNode);
584end;
585
586function TMap.PosToCell(Pos: TPoint): TCell;
587var
588 I: Integer;
589begin
590 Result := nil;
591 for I := 0 to Cells.Count - 1 do
592 if Cells[I].Terrain <> ttVoid then begin
593 if Cells[I].Polygon.IsPointInside(Pos) then begin
594 Result := Cells[I];
595 Exit;
596 end;
597 end;
598end;
599
600function TMap.CellToPos(Cell: TCell): TPoint;
601begin
602 Result := Cell.PosPx;
603end;
604
605procedure TMap.ComputePlayerStats;
606var
607 Cell: TCell;
608begin
609 for Cell in Cells do
610 with Cell do begin
611 if Assigned(Player) then
612 with TPlayer(Player) do begin
613 Inc(TotalCells);
614 Inc(TotalUnits, Power);
615 if Terrain = ttCity then
616 Inc(TotalCities);
617 if Extra = etObjectiveTarget then
618 Inc(TotalWinObjectiveCells);
619 end;
620 end;
621end;
622
623procedure TMap.Generate;
624var
625 X, Y: Integer;
626 NewCell: TCell;
627begin
628 Clear;
629
630 // Allocate and init new
631 Cells.Count := FSize.Y * FSize.X;
632 FNewCellId := 1;
633 for Y := 0 to FSize.Y - 1 do
634 for X := 0 to FSize.X - 1 do begin
635 NewCell := TCell.Create;
636 NewCell.Map := Self;
637 NewCell.PosPx := TPoint.Create(X * DefaultCellSize.X, Y * DefaultCellSize.Y);
638 NewCell.Id := GetNewCellId;
639 SetLength(NewCell.Polygon.Points, 1);
640 NewCell.Polygon.Points[0] := NewCell.PosPx;
641 Cells[Y * FSize.X + X] := NewCell;
642 end;
643 FPixelRect := FPixelRect;
644end;
645
646procedure TMap.MakeSymetric;
647var
648 C: Integer;
649 I: Integer;
650 CellLink: TCellLink;
651 OtherCell1: TCell;
652 OtherCell2: TCell;
653 OppositeCell: TCell;
654begin
655 // Generic way to create two sides symetric map independent to shape
656 for C := 0 to (Cells.Count div 2) - 1 do begin
657 Cells[C].Terrain := Cells[Cells.Count - 1 - C].Terrain;
658 Cells[C].Power := Cells[Cells.Count - 1 - C].Power;
659
660 for I := Cells[C].Links.Count - 1 downto 0 do begin
661 CellLink := Cells[C].Links[I];
662
663 // Remove cells on first half of the map
664 if (Cells.IndexOf(CellLink.Cells[0]) <= (Cells.Count div 2)) and
665 (Cells.IndexOf(CellLink.Cells[1]) <= (Cells.Count div 2)) then
666 begin
667 CellLinks.Remove(CellLink);
668 Continue;
669 end;
670
671 // Make cross half links symetric
672 if (Cells.IndexOf(CellLink.Cells[0]) <= (Cells.Count div 2)) and
673 (Cells.IndexOf(CellLink.Cells[1]) >= (Cells.Count div 2)) then begin
674 OtherCell1 := Cells[Cells.Count - 1 - Cells.IndexOf(CellLink.Cells[1])];
675 OtherCell2 := CellLink.Cells[1];
676 CellLinks.Remove(CellLink);
677 if not Assigned(CellLinks.FindByCells(OtherCell1, OtherCell2)) then
678 CellLinks.AddLink(OtherCell1, OtherCell2);
679 end else
680 if (Cells.IndexOf(CellLink.Cells[0]) >= (Cells.Count div 2)) and
681 (Cells.IndexOf(CellLink.Cells[1]) <= (Cells.Count div 2)) then begin
682 OtherCell1 := Cells[Cells.Count - 1 - Cells.IndexOf(CellLink.Cells[0])];
683 OtherCell2 := CellLink.Cells[0];
684 CellLinks.Remove(CellLink);
685 if not Assigned(CellLinks.FindByCells(OtherCell1, OtherCell2)) then
686 CellLinks.AddLink(OtherCell1, OtherCell2);
687 end;
688 end;
689 end;
690
691 for C := 0 to (Cells.Count div 2) - 1 do begin
692 // Make copy of links from second half
693 OppositeCell := Cells[Cells.Count - 1 - C];
694 for CellLink in OppositeCell.Links do
695 if (Cells.IndexOf(CellLink.Cells[0]) > (Cells.Count div 2)) and
696 (Cells.IndexOf(CellLink.Cells[1]) > (Cells.Count div 2)) then begin
697 OtherCell1 := Cells[Cells.Count - 1 - Cells.IndexOf(CellLink.Cells[0])];
698 OtherCell2 := Cells[Cells.Count - 1 - Cells.IndexOf(CellLink.Cells[1])];
699 if not Assigned(CellLinks.FindByCells(OtherCell1, OtherCell2)) then
700 CellLinks.AddLink(OtherCell1, OtherCell2);
701 end;
702 end;
703end;
704
705procedure TMap.CreateLinks;
706var
707 LastAreaCount: Integer;
708begin
709 BuildMapAreas;
710 LastAreaCount := -1;
711 while (Areas.Count > 1) and (Areas.Count <> LastAreaCount) do begin
712 LastAreaCount := Areas.Count;
713 BuildBridges;
714 BuildMapAreas;
715 end;
716end;
717
718procedure TMap.Clear;
719begin
720 CellLinks.Clear;
721 Cells.Clear;
722 FNewCellId := 1;
723 FPixelRect.SetEmpty;
724end;
725
726procedure TMap.CheckCells;
727var
728 I: Integer;
729 J: Integer;
730begin
731 for I := 0 to Cells.Count - 1 do begin
732 for J := I + 1 to Cells.Count - 1 do begin
733 if (Cells[I].Id = Cells[J].Id) then
734 raise Exception.Create('Duplicate cells ID ' + IntToStr(I) + ' ' + IntToStr(J));
735 if (Cells[I].PosPx = Cells[J].PosPx) then
736 raise Exception.Create('Duplicate cells position ' + IntToStr(I) + ' ' + IntToStr(J));
737 end;
738 end;
739end;
740
741constructor TMap.Create;
742begin
743 MaxPower := DefaultMaxPower;
744 DefaultCellSize := TPoint.Create(220, 220);
745 Cells := TCells.Create;
746 Cells.Map := Self;
747 Size := TPoint.Create(0, 0);
748 Image := TImage.Create(nil);
749 CellLinks := TCellLinks.Create;
750 CellLinks.Map := Self;
751 Areas := TMapAreas.Create;
752end;
753
754destructor TMap.Destroy;
755begin
756 Size := TPoint.Create(0, 0);
757 FreeAndNil(Areas);
758 FreeAndNil(CellLinks);
759 FreeAndNil(Image);
760 FreeAndNil(Cells);
761 inherited Destroy;
762end;
763
764function TMap.CalculatePixelRect: TRect;
765var
766 I: Integer;
767 CellRect: TRect;
768begin
769 Result := TRect.Create(TPoint.Create(0, 0), TPoint.Create(0, 0));
770 // This is generic algorithm to determine pixel size of entire map
771 for I := 0 to Cells.Count - 1 do begin
772 CellRect := Cells[I].Polygon.GetRect;
773 if I = 0 then Result := CellRect
774 else begin
775 Result.P1 := TPoint.Min(Result.P1, CellRect.P1);
776 Result.P2 := TPoint.Max(Result.P2, CellRect.P2);
777 end;
778 end;
779end;
780
781procedure TMap.ForEachCells(Method: TMethod);
782begin
783end;
784
785function TMap.SearchDifferentCellArea(List: TCells; SourceArea, DestArea: TMapArea): TCell;
786var
787 NewList: TCells;
788 NewListVoid: TCells;
789 I: Integer;
790 C: Integer;
791begin
792 Result := nil;
793 NewList := TCells.Create;
794 NewList.FreeObjects := False;
795 NewListVoid := TCells.Create;
796 NewListVoid.FreeObjects := False;
797
798 for C := 0 to List.Count - 1 do
799 with List[C] do begin
800 for I := 0 to Neighbors.Count - 1 do
801 with Neighbors[I] do
802 if (not Mark) and (Terrain <> ttVoid) and (Area <> SourceArea) and ((DestArea = nil) or (DestArea = Area)) then begin
803 NewList.Add(List[C].Neighbors[I]);
804 Mark := True;
805 end else
806 if (not Mark) and (Terrain = ttVoid) then begin
807 NewListVoid.Add(List[C].Neighbors[I]);
808 Mark := True;
809 end;
810 end;
811
812 if NewList.Count > 0 then begin
813 // We found cell with different area
814 Result := NewList[Random(NewList.Count)];
815 end else
816 if NewListVoid.Count > 0 then begin
817 // Cell was not found but we have more void cells to check
818 Result := SearchDifferentCellArea(NewListVoid, SourceArea, DestArea);
819 end;
820
821 FreeAndNil(NewListVoid);
822 FreeAndNil(NewList);
823end;
824
825procedure TMap.BuildBridges;
826var
827 List: TCells;
828 BorderList: TCells;
829 Cell: TCell;
830 FoundCell1: TCell;
831 FoundCell2: TCell;
832 I: Integer;
833 J: Integer;
834begin
835 List := TCells.Create;
836 List.FreeObjects := False;
837
838 BorderList := TCells.Create;
839 BorderList.FreeObjects := False;
840
841 // Build area bridges
842 if Areas.Count > 1 then
843 for I := 0 to Areas.Count - 1 do
844 with TMapArea(Areas[I]) do begin
845 GetBorderCells(BorderList);
846 if BorderList.Count > 0 then
847 for J := 0 to 4 do begin
848
849 Cell := BorderList[Random(BorderList.Count)];
850 List.Clear;
851 List.Add(Cell);
852
853 Map.Cells.ClearMark;
854
855 // Find nearest cell with different area
856 FoundCell1 := SearchDifferentCellArea(List, TMapArea(Map.Areas[I]), nil);
857 if Assigned(FoundCell1) then begin
858 // Again find back nearest cell with different area.
859 // This will ensure that both cells are closest ones
860
861 Map.Cells.ClearMark;
862 List[0] := FoundCell1;
863 FoundCell2 := SearchDifferentCellArea(List, FoundCell1.Area, TMapArea(Map.Areas[I]));
864 if Assigned(FoundCell2) then begin
865 // Check if link doesn't exist already
866 if not Assigned(FoundCell1.Links.FindByCells(FoundCell1, FoundCell2)) then begin
867 Map.CellLinks.AddLink(FoundCell1, FoundCell2);
868 Inc(BridgeCount);
869 end;
870 end;
871 end;
872 end;
873 end;
874 FreeAndNil(List);
875 FreeAndNil(BorderList);
876end;
877
878procedure TMap.BuildMapAreas;
879var
880 C: Integer;
881 NewArea: TMapArea;
882begin
883 for C := 0 to Cells.Count - 1 do
884 with Cells[C] do
885 Area := nil;
886 Areas.Clear;
887 for C := 0 to Cells.Count - 1 do
888 with Cells[C] do
889 if (Terrain <> ttVoid) and (not Assigned(Area)) then begin
890 NewArea := TMapArea.Create;
891 NewArea.Id := Map.Areas.Count;
892 NewArea.Map := Map;
893 Areas.Add(NewArea);
894 Area := NewArea;
895 AreaExtend;
896 end;
897end;
898
899{ TCell }
900
901procedure TCell.SetPower(AValue: Integer);
902begin
903 if FPower = AValue then Exit;
904 if AValue < 0 then
905 raise Exception.Create(SNegativeCellPowerNotAllowed);
906 FPower := AValue;
907 //Check;
908end;
909
910procedure TCell.ConnectTo(Cell: TCell);
911begin
912 if Cell = Self then
913 raise Exception.Create('Can''t connect map cell to itself');
914 // Connect only if already not connected
915 if Neighbors.IndexOf(Cell) < 0 then begin
916 Cell.Neighbors.Add(Self);
917 Neighbors.Add(Cell);
918 end;
919end;
920
921procedure TCell.DisconnectFrom(Cell: TCell);
922var
923 I: Integer;
924begin
925 I := Cell.Neighbors.IndexOf(Self);
926 if I >= 0 then Cell.Neighbors.Delete(I) else
927 raise Exception.Create('Can''t disconnect neigboring cells.');
928 I := Neighbors.IndexOf(Cell);
929 if I >= 0 then Neighbors.Delete(I)
930 else Exception.Create('Can''t disconnect neigboring cells.');
931end;
932
933function TCell.NeighboringToVoid: Boolean;
934var
935 NeighVoidCount: Integer;
936 NeighborCell: TCell;
937begin
938 NeighVoidCount := 0;
939 for NeighborCell in Neighbors do
940 if (NeighborCell.Terrain = ttVoid) then Inc(NeighVoidCount);
941 Result := NeighVoidCount > 0;
942end;
943
944procedure TCell.SetArea(AValue: TMapArea);
945begin
946 if FArea = AValue then Exit;
947 if Assigned(FArea) then FArea.Cells.Remove(Self);
948 FArea := AValue;
949 if Assigned(FArea) then FArea.Cells.Add(Self);
950end;
951
952procedure TCell.SetId(AValue: Integer);
953begin
954 if FId = AValue then Exit;
955 FId := AValue;
956end;
957
958procedure TCell.AreaExtend;
959var
960 NeighborCell: TCell;
961begin
962 for NeighborCell in Neighbors do
963 if (NeighborCell.Terrain <> ttVoid) and (not Assigned(NeighborCell.Area)) then begin
964 NeighborCell.Area := Area;
965 NeighborCell.AreaExtend;
966 end;
967end;
968
969procedure TCell.FixRefId;
970var
971 I: Integer;
972 Cell: TCell;
973begin
974 Player := TGame(Map.Game).Players.FindById(PlayerId);
975
976 Neighbors.Count := Length(NeighborsId);
977 for I := 0 to Length(NeighborsId) - 1 do
978 Neighbors[I] := nil;
979 for I := 0 to Length(NeighborsId) - 1 do begin
980 Cell := Map.Cells.FindById(NeighborsId[I]);
981 if not Assigned(Cell) then
982 raise Exception.Create('Neighbor cell id not found ' + IntToStr(NeighborsId[I]));
983 if Neighbors.IndexOf(Cell) <> -1 then
984 raise Exception.Create('Duplicate neighbor cell ' + IntToStr(NeighborsId[I]) + ' found for cell ' + IntToStr(Id));
985 Neighbors[I] := Cell;
986 end;
987end;
988
989procedure TCell.LoadFromNode(Node: TDOMNode);
990var
991 Node2: TDOMNode;
992 Node3: TDOMNode;
993begin
994 Id := ReadInteger(Node, 'Id', 0);
995 Power := ReadInteger(Node, 'Power', 0);
996 Terrain := TTerrainType(ReadInteger(Node, 'Terrain', Integer(ttVoid)));
997 Extra := TExtraType(ReadInteger(Node, 'Extra', Integer(etNone)));
998 PosPx.X := ReadInteger(Node, 'PosX', 0);
999 PosPx.Y := ReadInteger(Node, 'PosY', 0);
1000 PlayerId := ReadInteger(Node, 'Player', 0);
1001
1002 Node3 := Node.FindNode('Neighbours');
1003 if Assigned(Node3) then begin
1004 SetLength(NeighborsId, 0);
1005 Node2 := Node3.FirstChild;
1006 while Assigned(Node2) and (Node2.NodeName = 'Neighbour') do begin
1007 SetLength(NeighborsId, Length(NeighborsId) + 1);
1008 NeighborsId[High(NeighborsId)] := ReadInteger(Node2, 'Id', 0);
1009 Node2 := Node2.NextSibling;
1010 end;
1011 end;
1012
1013 Node3 := Node.FindNode('Polygon');
1014 if Assigned(Node3) then begin
1015 Polygon.Clear;
1016 Node2 := Node3.FirstChild;
1017 while Assigned(Node2) and (Node2.NodeName = 'Point') do begin
1018 Polygon.AddPoint(TPoint.Create(ReadInteger(Node2, 'X', 0), ReadInteger(Node2, 'Y', 0)));
1019 Node2 := Node2.NextSibling;
1020 end;
1021 end;
1022end;
1023
1024procedure TCell.SaveToNode(Node: TDOMNode);
1025var
1026 NewNode: TDOMNode;
1027 NewNode2: TDOMNode;
1028 I: Integer;
1029begin
1030 WriteInteger(Node, 'Id', Id);
1031 WriteInteger(Node, 'Power', Power);
1032 WriteInteger(Node, 'Terrain', Integer(Terrain));
1033 WriteInteger(Node, 'Extra', Integer(Extra));
1034 WriteInteger(Node, 'PosX', PosPx.X);
1035 WriteInteger(Node, 'PosY', PosPx.Y);
1036 if Assigned(Player) then
1037 WriteInteger(Node, 'Player', TPlayer(Player).Id)
1038 else WriteInteger(Node, 'Player', 0);
1039 NewNode := Node.OwnerDocument.CreateElement('Neighbours');
1040 Node.AppendChild(NewNode);
1041 for I := 0 to Neighbors.Count - 1 do begin
1042 NewNode2 := NewNode.OwnerDocument.CreateElement('Neighbour');
1043 NewNode.AppendChild(NewNode2);
1044 WriteInteger(NewNode2, 'Id', Neighbors[I].Id);
1045 end;
1046 NewNode := Node.OwnerDocument.CreateElement('Polygon');
1047 Node.AppendChild(NewNode);
1048 for I := 0 to Length(Polygon.Points) - 1 do begin
1049 NewNode2 := NewNode.OwnerDocument.CreateElement('Point');
1050 NewNode.AppendChild(NewNode2);
1051 WriteInteger(NewNode2, 'X', Polygon.Points[I].X);
1052 WriteInteger(NewNode2, 'Y', Polygon.Points[I].Y);
1053 end;
1054end;
1055
1056procedure TCell.Assign(Source: TCell);
1057begin
1058 Id := Source.Id;
1059 PosPx := Source.PosPx;
1060 Terrain := Source.Terrain;
1061 Polygon := Source.Polygon;
1062 Player := Source.Player;
1063 Mark := Source.Mark;
1064 // TODO: How to copy neighbours and moves list
1065end;
1066
1067function TCell.GetColor: TColor;
1068begin
1069 if Assigned(Player) then Result := TPlayer(Player).Color
1070 else Result := clGray;
1071end;
1072
1073function TCell.ToString: ansistring;
1074begin
1075 Result := IntToStr(Id);
1076end;
1077
1078constructor TCell.Create;
1079begin
1080 FId := -1;
1081 Player := nil;
1082 Neighbors := TCells.Create;
1083 Neighbors.FreeObjects := False;
1084 Links := TCellLinks.Create;
1085 Links.FreeObjects := False;
1086end;
1087
1088destructor TCell.Destroy;
1089var
1090 I: Integer;
1091begin
1092 for I := Links.Count - 1 downto 0 do
1093 FMap.CellLinks.Remove(Links[I]);
1094 FreeAndNil(Links);
1095 for I := Neighbors.Count - 1 downto 0 do
1096 if Neighbors[I].Neighbors.Remove(Self) = -1 then
1097 raise Exception.Create(SCellRemoveNeighborError);
1098 FreeAndNil(Neighbors);
1099 inherited Destroy;
1100end;
1101
1102end.
1103
Note: See TracBrowser for help on using the repository browser.