1 | unit UMap;
|
---|
2 |
|
---|
3 | {$mode delphi}
|
---|
4 |
|
---|
5 | interface
|
---|
6 |
|
---|
7 | uses
|
---|
8 | Classes, SysUtils, UGame, XMLRead, XMLWrite, DOM, UGeometry, fgl;
|
---|
9 |
|
---|
10 | type
|
---|
11 | TCellsDistance = class
|
---|
12 | Cell1: TCell;
|
---|
13 | Cell2: TCell;
|
---|
14 | Distance: Double;
|
---|
15 | end;
|
---|
16 |
|
---|
17 | { THexMap }
|
---|
18 |
|
---|
19 | THexMap = class(TMap)
|
---|
20 | private
|
---|
21 | function IsCellsPosNeighbor(CellPos1, CellPos2: TPoint): Boolean;
|
---|
22 | procedure GetCellPosNeighbors(CellPos: TPoint; Neighbours: TCells);
|
---|
23 | function GetHexagonPolygon(Pos: TPoint; Size: TPoint): TPolygon;
|
---|
24 | public
|
---|
25 | procedure LoadFromFile(FileName: string); override;
|
---|
26 | procedure SaveToFile(FileName: string); override;
|
---|
27 | function IsValidIndex(Index: TPoint): Boolean; override;
|
---|
28 | procedure Generate; override;
|
---|
29 | end;
|
---|
30 |
|
---|
31 | { TSquareMap }
|
---|
32 |
|
---|
33 | TSquareMap = class(TMap)
|
---|
34 | private
|
---|
35 | function GetSquarePolygon(Pos: TPoint; Size: TPoint): TPolygon;
|
---|
36 | public
|
---|
37 | function IsValidIndex(Index: TPoint): Boolean; override;
|
---|
38 | procedure Generate; override;
|
---|
39 | end;
|
---|
40 |
|
---|
41 | { TTriangleMap }
|
---|
42 |
|
---|
43 | TTriangleMap = class(TMap)
|
---|
44 | private
|
---|
45 | function GetTrianglePolygon(Pos: TPoint; Size: TPoint; Reverse: Boolean): TPolygon;
|
---|
46 | public
|
---|
47 | function IsValidIndex(Index: TPoint): Boolean; override;
|
---|
48 | procedure Generate; override;
|
---|
49 | end;
|
---|
50 |
|
---|
51 | { TVoronoiMap }
|
---|
52 |
|
---|
53 | TVoronoiMap = class(TMap)
|
---|
54 | private
|
---|
55 | public
|
---|
56 | procedure Generate; override;
|
---|
57 | end;
|
---|
58 |
|
---|
59 | implementation
|
---|
60 |
|
---|
61 | { THexMap }
|
---|
62 |
|
---|
63 | function THexMap.GetHexagonPolygon(Pos: TPoint; Size: TPoint): TPolygon;
|
---|
64 | var
|
---|
65 | Shift: TPointF;
|
---|
66 | begin
|
---|
67 | Shift := TPointF.Create(0.5 * cos(30 / 180 * Pi), 0.5 * sin(30 / 180 * Pi));
|
---|
68 | SetLength(Result.Points, 6);
|
---|
69 | Result.Points[0] := TPoint.Create(Trunc(Pos.X + 0 * Size.X), Trunc(Pos.Y - 0.5 * Size.Y));
|
---|
70 | Result.Points[1] := TPoint.Create(Trunc(Pos.X + Shift.X * Size.X), Trunc(Pos.Y - Shift.Y * Size.Y));
|
---|
71 | Result.Points[2] := TPoint.Create(Trunc(Pos.X + Shift.X * Size.X), Trunc(Pos.Y + Shift.Y * Size.Y));
|
---|
72 | Result.Points[3] := TPoint.Create(Trunc(Pos.X + 0 * Size.X), Trunc(Pos.Y + 0.5 * Size.Y));
|
---|
73 | Result.Points[4] := TPoint.Create(Trunc(Pos.X - Shift.X * Size.X), Trunc(Pos.Y + Shift.Y * Size.Y));
|
---|
74 | Result.Points[5] := TPoint.Create(Trunc(Pos.X - Shift.X * Size.X), Trunc(Pos.Y - Shift.Y * Size.Y));
|
---|
75 | end;
|
---|
76 |
|
---|
77 | function THexMap.IsCellsPosNeighbor(CellPos1, CellPos2: TPoint): Boolean;
|
---|
78 | var
|
---|
79 | DX: Integer;
|
---|
80 | DY: Integer;
|
---|
81 | MinY: Integer;
|
---|
82 | begin
|
---|
83 | if CellPos1.Y < CellPos2.Y then MinY:= CellPos1.Y
|
---|
84 | else MinY := CellPos2.Y;
|
---|
85 | DX := CellPos2.X - CellPos1.X;
|
---|
86 | DY := CellPos2.Y - CellPos1.Y;
|
---|
87 | Result := (Abs(DX) <= 1) and (Abs(DY) <= 1) and
|
---|
88 | ((((MinY mod 2) = 1) and
|
---|
89 | not ((DX = 1) and (DY = -1)) and
|
---|
90 | not ((DX = -1) and (DY = 1))) or
|
---|
91 | (((MinY mod 2) = 0) and
|
---|
92 | not ((DX = -1) and (DY = -1)) and
|
---|
93 | not ((DX = 1) and (DY = 1))));
|
---|
94 | Result := Result and not ((CellPos1.X = CellPos2.X) and (CellPos1.Y = CellPos2.Y));
|
---|
95 | end;
|
---|
96 |
|
---|
97 | procedure THexMap.LoadFromFile(FileName: string);
|
---|
98 | var
|
---|
99 | Doc: TXMLDocument;
|
---|
100 | begin
|
---|
101 | try
|
---|
102 | ReadXMLFile(Doc, FileName);
|
---|
103 | if Doc.DocumentElement.TagName <> 'Map' then
|
---|
104 | raise Exception.Create('Invalid map format');
|
---|
105 | finally
|
---|
106 | Doc.Free;
|
---|
107 | end;
|
---|
108 | inherited LoadFromFile(FileName);
|
---|
109 | end;
|
---|
110 |
|
---|
111 | procedure THexMap.SaveToFile(FileName: string);
|
---|
112 | var
|
---|
113 | Doc: TXMLDocument;
|
---|
114 | RootNode: TDOMNode;
|
---|
115 | begin
|
---|
116 | try
|
---|
117 | Doc := TXMLDocument.Create;
|
---|
118 | RootNode := Doc.CreateElement('Map');
|
---|
119 | Doc.Appendchild(RootNode);
|
---|
120 | WriteXMLFile(Doc, FileName);
|
---|
121 | finally
|
---|
122 | Doc.Free;
|
---|
123 | end;
|
---|
124 | inherited SaveToFile(FileName);
|
---|
125 | end;
|
---|
126 |
|
---|
127 | function THexMap.IsValidIndex(Index: TPoint): Boolean;
|
---|
128 | begin
|
---|
129 | Result := (Index.X >= 0) and (Index.X < Size.X) and
|
---|
130 | (Index.Y >= 0) and (Index.Y < Size.Y);
|
---|
131 | end;
|
---|
132 |
|
---|
133 | procedure THexMap.GetCellPosNeighbors(CellPos: TPoint; Neighbours: TCells);
|
---|
134 | var
|
---|
135 | X, Y: Integer;
|
---|
136 | begin
|
---|
137 | Neighbours.Count := 0;
|
---|
138 | for Y := -1 to 1 do
|
---|
139 | for X := -1 to 1 do
|
---|
140 | if IsValidIndex(TPoint.Create(CellPos.X + X, CellPos.Y + Y)) and
|
---|
141 | IsCellsPosNeighbor(CellPos, TPoint.Create((CellPos.X + X), (CellPos.Y + Y))) then begin
|
---|
142 | Neighbours.Add(TCell(Cells[(CellPos.Y + Y) * Size.X + (CellPos.X + X)]));
|
---|
143 | end;
|
---|
144 | end;
|
---|
145 |
|
---|
146 | procedure THexMap.Generate;
|
---|
147 | var
|
---|
148 | X, Y: Integer;
|
---|
149 | NewCell: TCell;
|
---|
150 | PX, PY: Double;
|
---|
151 | begin
|
---|
152 | Clear;
|
---|
153 |
|
---|
154 | // Allocate and init new
|
---|
155 | Cells.Count := Size.Y * Size.X;
|
---|
156 | for Y := 0 to Size.Y - 1 do
|
---|
157 | for X := 0 to Size.X - 1 do begin
|
---|
158 | NewCell := TCell.Create;
|
---|
159 | NewCell.Map := Self;
|
---|
160 | PX := X;
|
---|
161 | PY := Y;
|
---|
162 | if (Y and 1) = 1 then begin
|
---|
163 | PX := PX + 0.5;
|
---|
164 | //Y := Y + 0.5;
|
---|
165 | end;
|
---|
166 | NewCell.PosPx := TPoint.Create(Trunc(PX * DefaultCellSize.X / HexCellMulX),
|
---|
167 | Trunc(PY * DefaultCellSize.Y / HexCellMulY));
|
---|
168 | NewCell.Polygon := GetHexagonPolygon(NewCell.PosPx, DefaultCellSize);
|
---|
169 | NewCell.Id := GetNewCellId;
|
---|
170 | Cells[Y * Size.X + X] := NewCell;
|
---|
171 | end;
|
---|
172 |
|
---|
173 | // Generate neightbours
|
---|
174 | for Y := 0 to Size.Y - 1 do
|
---|
175 | for X := 0 to Size.X - 1 do
|
---|
176 | with TCell(Cells[Y * Size.X + X]) do begin
|
---|
177 | GetCellPosNeighbors(TPoint.Create(X, Y), Neighbors);
|
---|
178 | end;
|
---|
179 |
|
---|
180 | FPixelRect := CalculatePixelRect;
|
---|
181 | end;
|
---|
182 |
|
---|
183 |
|
---|
184 | { TSquareMap }
|
---|
185 |
|
---|
186 | procedure TSquareMap.Generate;
|
---|
187 | var
|
---|
188 | X, Y: Integer;
|
---|
189 | NewCell: TCell;
|
---|
190 | begin
|
---|
191 | Clear;
|
---|
192 |
|
---|
193 | // Allocate and init new
|
---|
194 | Cells.Count := Size.Y * Size.X;
|
---|
195 | for Y := 0 to Size.Y - 1 do
|
---|
196 | for X := 0 to Size.X - 1 do begin
|
---|
197 | NewCell := TCell.Create;
|
---|
198 | NewCell.Map := Self;
|
---|
199 | NewCell.PosPx := TPoint.Create(Trunc(X * DefaultCellSize.X * SquareCellMulX),
|
---|
200 | Trunc(Y * DefaultCellSize.Y * SquareCellMulY));
|
---|
201 | NewCell.Polygon := GetSquarePolygon(NewCell.PosPx, DefaultCellSize);
|
---|
202 | NewCell.Id := GetNewCellId;
|
---|
203 | Cells[Y * Size.X + X] := NewCell;
|
---|
204 | end;
|
---|
205 |
|
---|
206 | // Generate neighbours
|
---|
207 | for Y := 0 to Size.Y - 1 do
|
---|
208 | for X := 0 to Size.X - 1 do
|
---|
209 | with TCell(Cells[Y * Size.X + X]) do begin
|
---|
210 | if IsValidIndex(TPoint.Create(X + 1, Y + 0)) then
|
---|
211 | Neighbors.Add(TCell(Cells[(Y + 0) * Size.X + (X + 1)]));
|
---|
212 | if IsValidIndex(TPoint.Create(X + 0, Y + 1)) then
|
---|
213 | Neighbors.Add(TCell(Cells[(Y + 1) * Size.X + (X + 0)]));
|
---|
214 | if IsValidIndex(TPoint.Create(X - 1, Y + 0)) then
|
---|
215 | Neighbors.Add(TCell(Cells[(Y + 0) * Size.X + (X - 1)]));
|
---|
216 | if IsValidIndex(TPoint.Create(X + 0, Y - 1)) then
|
---|
217 | Neighbors.Add(TCell(Cells[(Y - 1) * Size.X + (X + 0)]));
|
---|
218 | end;
|
---|
219 |
|
---|
220 | FPixelRect := CalculatePixelRect;
|
---|
221 | end;
|
---|
222 |
|
---|
223 | function TSquareMap.IsValidIndex(Index: TPoint): Boolean;
|
---|
224 | begin
|
---|
225 | Result := (Index.X >= 0) and (Index.X < Size.X) and
|
---|
226 | (Index.Y >= 0) and (Index.Y < Size.Y);
|
---|
227 | end;
|
---|
228 |
|
---|
229 | function TSquareMap.GetSquarePolygon(Pos: TPoint; Size: TPoint): TPolygon;
|
---|
230 | begin
|
---|
231 | SetLength(Result.Points, 4);
|
---|
232 | Result.Points[0] := TPoint.Create(Trunc(Pos.X - Size.X / 2), Trunc(Pos.Y - Size.Y / 2));
|
---|
233 | Result.Points[1] := TPoint.Create(Trunc(Pos.X + Size.X / 2), Trunc(Pos.Y - Size.Y / 2));
|
---|
234 | Result.Points[2] := TPoint.Create(Trunc(Pos.X + Size.X / 2), Trunc(Pos.Y + Size.Y / 2));
|
---|
235 | Result.Points[3] := TPoint.Create(Trunc(Pos.X - Size.X / 2), Trunc(Pos.Y + Size.Y / 2));
|
---|
236 | end;
|
---|
237 |
|
---|
238 | { TVoronoiMap }
|
---|
239 |
|
---|
240 | function CompareDistance(const C1, C2: TCellsDistance): Integer;
|
---|
241 | begin
|
---|
242 | if C1.Distance > C2.Distance then Result := 1
|
---|
243 | else if C1.Distance < C2.Distance then Result := -1
|
---|
244 | else Result := 0;
|
---|
245 | end;
|
---|
246 |
|
---|
247 | procedure TVoronoiMap.Generate;
|
---|
248 | var
|
---|
249 | X, Y: Integer;
|
---|
250 | NewCell: TCell;
|
---|
251 | I, J: Integer;
|
---|
252 | Cell: TCell;
|
---|
253 | Cell2: TCell;
|
---|
254 | L1: TLine;
|
---|
255 | MP: TPoint;
|
---|
256 | LinkLine: TLine;
|
---|
257 | const
|
---|
258 | CellGapWidth = 4;
|
---|
259 | begin
|
---|
260 | Clear;
|
---|
261 | //RandSeed := 1234;
|
---|
262 |
|
---|
263 | // Allocate and init new cells
|
---|
264 | Cells.Count := Size.Y * Size.X;
|
---|
265 | for Y := 0 to Size.Y - 1 do
|
---|
266 | for X := 0 to Size.X - 1 do begin
|
---|
267 | NewCell := TCell.Create;
|
---|
268 | NewCell.Map := Self;
|
---|
269 | NewCell.PosPx := TPoint.Create(Trunc(Random * Size.X * DefaultCellSize.X),
|
---|
270 | Trunc(Random * Size.Y * DefaultCellSize.Y));
|
---|
271 | SetLength(NewCell.Polygon.Points, 1);
|
---|
272 | NewCell.Polygon.Points[0] := NewCell.PosPx;
|
---|
273 | NewCell.Id := GetNewCellId;
|
---|
274 | Cells[Y * Size.X + X] := NewCell;
|
---|
275 | end;
|
---|
276 |
|
---|
277 | // Compute polygon cat out by all other cells
|
---|
278 | for Cell in Cells do begin
|
---|
279 | Cell.Polygon := TPolygon.Create(TRect.Create(TPoint.Create(0, 0),
|
---|
280 | TPoint.Create(Size.X * DefaultCellSize.X, Size.Y * DefaultCellSize.Y)));
|
---|
281 | for Cell2 in Cells do
|
---|
282 | if Cell2 <> Cell then begin
|
---|
283 | LinkLine := TLine.Create(Cell.PosPx, Cell2.PosPx);
|
---|
284 | LinkLine.Distance := LinkLine.Distance - CellGapWidth;
|
---|
285 | MP := LinkLine.GetMiddle;
|
---|
286 | // Create half plane vector
|
---|
287 | L1 := TLine.Create(MP, TPoint.Create(MP.X + LinkLine.GetSize.X,
|
---|
288 | MP.Y + LinkLine.GetSize.Y));
|
---|
289 |
|
---|
290 | Cell.Polygon.CutLine(L1, Cell.PosPx);
|
---|
291 | end;
|
---|
292 | end;
|
---|
293 |
|
---|
294 | // Link all cells with neighboring polygon edges
|
---|
295 | for I := 0 to Cells.Count - 1 do begin
|
---|
296 | for J := I + 1 to Cells.Count - 1 do begin
|
---|
297 | if Cells[I].Polygon.EdgeDistance(Cells[J].Polygon) < 2 * CellGapWidth then
|
---|
298 | Cells[I].ConnectTo(Cells[J]);
|
---|
299 | end;
|
---|
300 | end;
|
---|
301 |
|
---|
302 | FPixelRect := CalculatePixelRect;
|
---|
303 | end;
|
---|
304 |
|
---|
305 | { TTriangleMap }
|
---|
306 |
|
---|
307 | function TTriangleMap.GetTrianglePolygon(Pos: TPoint; Size: TPoint;
|
---|
308 | Reverse: Boolean): TPolygon;
|
---|
309 | var
|
---|
310 | Rev: Integer;
|
---|
311 | begin
|
---|
312 | if Reverse then Rev := -1
|
---|
313 | else Rev := 1;
|
---|
314 | SetLength(Result.Points, 3);
|
---|
315 | Result.Points[0] := TPoint.Create(Trunc(Pos.X - Size.X / 2), Trunc(Pos.Y - (Size.Y * 0.8) / 2 * Rev));
|
---|
316 | Result.Points[1] := TPoint.Create(Trunc(Pos.X + Size.X / 2), Trunc(Pos.Y - (Size.Y * 0.8) / 2 * Rev));
|
---|
317 | Result.Points[2] := TPoint.Create(Trunc(Pos.X), Trunc(Pos.Y + (Size.Y * 1.2) / 2 * Rev));
|
---|
318 | end;
|
---|
319 |
|
---|
320 | function TTriangleMap.IsValidIndex(Index: TPoint): Boolean;
|
---|
321 | begin
|
---|
322 | Result := (Index.X >= 0) and (Index.X < Size.X) and
|
---|
323 | (Index.Y >= 0) and (Index.Y < Size.Y);
|
---|
324 | end;
|
---|
325 |
|
---|
326 | procedure TTriangleMap.Generate;
|
---|
327 | var
|
---|
328 | X, Y: Integer;
|
---|
329 | Rev: Integer;
|
---|
330 | Reverse: Boolean;
|
---|
331 | NewCell: TCell;
|
---|
332 | begin
|
---|
333 | Clear;
|
---|
334 |
|
---|
335 | // Allocate and init new
|
---|
336 | Cells.Count := Size.Y * Size.X;
|
---|
337 | for Y := 0 to Size.Y - 1 do
|
---|
338 | for X := 0 to Size.X - 1 do begin
|
---|
339 | NewCell := TCell.Create;
|
---|
340 | NewCell.Map := Self;
|
---|
341 | Reverse := Boolean(X mod 2) xor Boolean(Y mod 2);
|
---|
342 | if Reverse then Rev := -1
|
---|
343 | else Rev := 1;
|
---|
344 | NewCell.PosPx := TPoint.Create(Trunc(X * DefaultCellSize.X * TriangleCellMulX),
|
---|
345 | Trunc((Y * DefaultCellSize.Y * TriangleCellMulY) - (0.1 * Rev * DefaultCellSize.Y)));
|
---|
346 | NewCell.Polygon := GetTrianglePolygon(NewCell.PosPx, DefaultCellSize, Reverse);
|
---|
347 | NewCell.Id := GetNewCellId;
|
---|
348 | Cells[Y * Size.X + X] := NewCell;
|
---|
349 | end;
|
---|
350 |
|
---|
351 | // Generate neighbours
|
---|
352 | for Y := 0 to Self.Size.Y - 1 do
|
---|
353 | for X := 0 to Size.X - 1 do
|
---|
354 | with TCell(Cells[Y * Size.X + X]) do begin
|
---|
355 | if Boolean(X mod 2) xor Boolean(Y mod 2) then Rev := -1
|
---|
356 | else Rev := 1;
|
---|
357 | if IsValidIndex(TPoint.Create(X + 1, Y + 0)) then
|
---|
358 | Neighbors.Add(TCell(Cells[(Y + 0) * Size.X + (X + 1)]));
|
---|
359 | if IsValidIndex(TPoint.Create(X + 0, Y - 1 * Rev)) then
|
---|
360 | Neighbors.Add(TCell(Cells[(Y - 1 * Rev) * Size.X + (X + 0)]));
|
---|
361 | if IsValidIndex(TPoint.Create(X - 1, Y + 0)) then
|
---|
362 | Neighbors.Add(TCell(Cells[(Y + 0) * Size.X + (X - 1)]));
|
---|
363 | end;
|
---|
364 |
|
---|
365 | FPixelRect := CalculatePixelRect;
|
---|
366 | end;
|
---|
367 |
|
---|
368 |
|
---|
369 | end.
|
---|
370 |
|
---|