source: tags/1.4.0/Player.pas

Last change on this file was 401, checked in by chronos, 2 weeks ago

Merged revision(s) 396-400 from trunk:

  • Fixed: Charts form chart was not drawn on the initial show.
  • Fixed: Run Randomize only once at startup.
  • Fixed: Store current state of RandSeed to game save.
  • Fixed: Load/store RandSeed as its Cardinal type instead of Integer.
  • Modified: Maximum number of player set to 12.
  • Fixed: Standard yellow color is too bright and not readable with white text. Used darker yellow instead.
  • Fixed: Integrity checks for id references loaded from game file.
  • Fixed: Wrong players and units id recalculation during game save causing bad references.
  • Modified: Set new created players as computer.
  • Fixed: Scale charts line width.
  • Fixed: Item form made bigger to avoid showing scrollbars.
  • Fixed: Item form string control made shorter.
File size: 37.0 KB
Line 
1unit Player;
2
3interface
4
5uses
6 Classes, SysUtils, Graphics, Map, DOM, Generics.Collections, Generics.Defaults,
7 XMLConf, XML, Math, Geometry, Units, Nation, ItemList, TurnStats;
8
9type
10 TUnitMoves = class;
11 TPlayerCells = class;
12 TPlayerMap = class;
13 TPlayer = class;
14
15 { TPlayerCell }
16
17 TPlayerCell = class
18 MovesFrom: TUnitMoves;
19 MovesTo: TUnitMoves;
20 Explored: Boolean;
21 InVisibleRange: Boolean;
22 MapCell: TCell;
23 List: TPlayerCells;
24 Neighbors: TPlayerCells;
25 procedure ConnectTo(Cell: TPlayerCell);
26 procedure DisconnectFrom(Cell: TPlayerCell);
27 function GetAvialPower: Integer;
28 function GetAvialPowerForMove: Integer;
29 function GetAttackPower: Integer;
30 procedure LoadFromNode(Node: TDOMNode);
31 procedure SaveToNode(Node: TDOMNode);
32 constructor Create;
33 destructor Destroy; override;
34 end;
35
36 { TPlayerCells }
37
38 TPlayerCells = class(TObjectList<TPlayerCell>)
39 Map: TPlayerMap;
40 function FindByCellId(Id: Integer): TPlayerCell;
41 function SearchCell(Cell: TCell): TPlayerCell;
42 procedure LoadFromNode(Node: TDOMNode);
43 procedure SaveToNode(Node: TDOMNode);
44 end;
45
46 { TPlayerMap }
47
48 TPlayerMap = class
49 Cells: TPlayerCells;
50 Player: TPlayer;
51 function PosToCell(Pos: TPoint): TPlayerCell; virtual;
52 function CellToPos(Cell: TPlayerCell): TPoint; virtual;
53 procedure LoadFromNode(Node: TDOMNode);
54 procedure SaveToNode(Node: TDOMNode);
55 procedure Update;
56 procedure Clear;
57 constructor Create;
58 destructor Destroy; override;
59 procedure CheckVisibility;
60 end;
61
62 TPlayerMode = (pmHuman, pmComputer);
63 TComputerAgressivity = (caLow, caMedium, caHigh);
64 TUnitMove = class;
65
66 TMoveEvent = procedure(CellFrom, CellTo: TPlayerCell; var CountOnce, CountRepeat: Integer;
67 Update: Boolean; var Confirm: Boolean) of object;
68 TMoveUpdatedEvent = procedure(UnitMove: TUnitMove) of object;
69
70 { TPlayer }
71
72 TPlayer = class(TItem)
73 private
74 FGame: TObject; //TGame;
75 FMode: TPlayerMode;
76 FOnMove: TMoveEvent;
77 procedure MoveUnit(UnitMove: TUnitMove);
78 procedure SetGame(AValue: TObject); // TGame
79 procedure Attack(var AttackPower, DefendPower: Integer);
80 procedure ClearMovesFromCell(Cell: TPlayerCell);
81 procedure CheckCounterMove(Move: TUnitMove);
82 procedure SetMode(AValue: TPlayerMode);
83 public
84 Color: TColor;
85 TotalUnits: Integer;
86 TotalCells: Integer;
87 TotalCities: Integer;
88 TotalDiscovered: Integer;
89 TotalWinObjectiveCells: Integer;
90 StartUnits: Integer;
91 StartCell: TCell;
92 PlayerMap: TPlayerMap;
93 Defensive: Boolean;
94 Agressivity: TComputerAgressivity;
95 TurnStats: TGameTurnStats;
96 Moves: TUnitMoves;
97 Units: TUnits;
98 Nation: TNation;
99 class function GetFields: TItemFields; override;
100 procedure GetValue(Index: Integer; out Value); override;
101 procedure SetValue(Index: Integer; var Value); override;
102 function GetReferenceList(Index: Integer): TBaseItemList; override;
103 class function GetClassSysName: string; override;
104 class function GetClassName: string; override;
105 function IsAllowedMoveTarget(CellFrom, CellTo: TPlayerCell): Boolean;
106 procedure ReduceMovesPower;
107 procedure RemoveInvalidMoves;
108 procedure RemoveEmptyUnits;
109 procedure InitUnitMoves;
110 procedure UpdateRepeatMoves;
111 procedure UpdateEmptyCellsNeutral;
112 procedure MoveAll;
113 procedure Grow;
114 function SetMove(CellFrom, CellTo: TPlayerCell; Power: Integer; Confirmation: Boolean = True): TUnitMove;
115 procedure Reset;
116 procedure Surrender;
117 function IsAlive: Boolean;
118 procedure Clear;
119 procedure LoadFromNode(Node: TDOMNode); override;
120 procedure SaveToNode(Node: TDOMNode); override;
121 constructor Create; override;
122 destructor Destroy; override;
123 procedure Assign(Source: TItem); override;
124 procedure LoadConfig(Config: TXmlConfig; Path: string);
125 procedure SaveConfig(Config: TXmlConfig; Path: string);
126 property Game: TObject read FGame write SetGame; // TGame
127 property Mode: TPlayerMode read FMode write SetMode;
128 property OnMove: TMoveEvent read FOnMove write FOnMove;
129 end;
130
131 TPlayerEvent = procedure (Player: TPlayer) of object;
132 TPlayerArray = array of TPlayer;
133
134 { TPlayers }
135
136 TPlayers = class(TItemList<TPlayer>)
137 public
138 Game: TObject; //TGame;
139 function GetPlayerByColor(Color: TColor): TPlayer;
140 function GetUnusedColor: TColor;
141 procedure Assign(Source: TItemList<TPlayer>); override;
142 procedure New(Name: string; Color: TColor; Mode: TPlayerMode);
143 function CreateItem(Name: string = ''): TPlayer; override;
144 function GetFirstHuman: TPlayer;
145 procedure LoadConfig(Config: TXmlConfig; Path: string);
146 procedure SaveConfig(Config: TXmlConfig; Path: string);
147 function GetAliveCount: Integer;
148 procedure GetAlivePlayers(Players: TPlayers); overload;
149 function GetAlivePlayers: TPlayerArray; overload;
150 function GetAlivePlayersWithCities: TPlayerArray;
151 end;
152
153 { TUnitMove }
154
155 TUnitMove = class
156 private
157 FCellFrom: TPlayerCell;
158 FCellTo: TPlayerCell;
159 procedure SetCellFrom(AValue: TPlayerCell);
160 procedure SetCellTo(AValue: TPlayerCell);
161 public
162 List: TUnitMoves;
163 CountOnce: Integer;
164 CountRepeat: Integer;
165 procedure Assign(Source: TUnitMove);
166 procedure LoadFromNode(Node: TDOMNode);
167 procedure SaveToNode(Node: TDOMNode);
168 constructor Create;
169 destructor Destroy; override;
170 property CellFrom: TPlayerCell read FCellFrom write SetCellFrom;
171 property CellTo: TPlayerCell read FCellTo write SetCellTo;
172 end;
173
174 { TUnitMoves }
175
176 TUnitMoves = class(TObjectList<TUnitMove>)
177 Game: TObject; //TGame;
178 Player: TPlayer;
179 procedure Assign(Source: TUnitMoves);
180 function SearchByFromTo(CellFrom, CellTo: TPlayerCell): TUnitMove;
181 procedure LoadFromNode(Node: TDOMNode);
182 procedure SaveToNode(Node: TDOMNode);
183 end;
184
185function CellCompare(constref Item1, Item2: TPlayerCell): Integer;
186function CellCompareDescending(constref Item1, Item2: TPlayerCell): Integer;
187
188
189implementation
190
191uses
192 Game, GameSystem, Building, UnitKind;
193
194resourcestring
195 SAttackerPowerPositive = 'Attacker power have to be higher then 0.';
196 SDefenderPowerPositive = 'Defender power have to be higher then or equal to 0.';
197 SUnfinishedBattle = 'Unfinished battle';
198 SLow = 'Low';
199 SMedium = 'Medium';
200 SHigh = 'High';
201 SHuman = 'Human';
202 SComputer = 'Computer';
203 SNation = 'Nation';
204 SMode = 'Mode';
205 SColor = 'Color';
206 SAgressivity = 'Agressivity';
207 SStartUnits = 'Start units';
208 SDefensive = 'Defensive';
209
210const
211 clDarkYellow = $00C0C0;
212 PlayerColors: array[0..11] of TColor = (clBlue, clRed, clGreen, clDarkYellow,
213 clFuchsia, clAqua, clOlive, clMaroon, clNavy, clPurple, clTeal, clGray);
214
215function ComparePointer(constref Item1, Item2: Integer): Integer;
216begin
217 Result := -CompareValue(Item1, Item2);
218end;
219
220
221{ TPlayerCell }
222
223procedure TPlayerCell.LoadFromNode(Node: TDOMNode);
224var
225 C: TCell;
226 CellId: Integer;
227begin
228 Explored := ReadBoolean(Node, 'Explored', False);
229 CellId := ReadInteger(Node, 'MapCell', 0);
230 C := TGame(List.Map.Player.Game).Map.Cells.FindById(CellId);
231 if not Assigned(C) then
232 raise Exception.Create('Cell map not found ' + IntToStr(CellId));
233 if List.SearchCell(C) <> nil then
234 raise Exception.Create('Map cell used twice ' + IntToStr(CellId));
235 MapCell := C;
236end;
237
238procedure TPlayerCell.ConnectTo(Cell: TPlayerCell);
239begin
240 if Cell = Self then
241 raise Exception.Create('Can''t connect player cell to itself');
242 if (Cell.Neighbors.IndexOf(Self) = -1) and
243 (Neighbors.IndexOf(Cell) = -1) then begin
244 Cell.Neighbors.Add(Self);
245 Neighbors.Add(Cell);
246 end;
247end;
248
249procedure TPlayerCell.DisconnectFrom(Cell: TPlayerCell);
250var
251 I: Integer;
252begin
253 I := Cell.Neighbors.IndexOf(Self);
254 if I >= 0 then Cell.Neighbors.Delete(I) else
255 raise Exception.Create('Can''t disconnect neigboring cells.');
256 I := Neighbors.IndexOf(Cell);
257 if I >= 0 then Neighbors.Delete(I)
258 else Exception.Create('Can''t disconnect neigboring cells.');
259end;
260
261function TPlayerCell.GetAvialPower: Integer;
262var
263 UnitMove: TUnitMove;
264begin
265 Result := 0;
266 if Assigned(MapCell.OneUnit) then
267 Result := MapCell.OneUnit.Power;
268 for UnitMove in MovesFrom do
269 Result := Result - UnitMove.CountOnce;
270end;
271
272function TPlayerCell.GetAvialPowerForMove: Integer;
273var
274 UnitMove: TUnitMove;
275begin
276 Result := 0;
277 if Assigned(MapCell.OneUnit) then
278 if MapCell.OneUnit.Moves > 0 then Result := MapCell.OneUnit.Power;
279 for UnitMove in MovesFrom do
280 Result := Result - UnitMove.CountOnce;
281end;
282
283function TPlayerCell.GetAttackPower: Integer;
284var
285 I: Integer;
286begin
287 Result := 0;
288 for I := 0 to MovesTo.Count - 1 do
289 Result := Result + TUnitMove(MovesTo[I]).CountOnce;
290end;
291
292procedure TPlayerCell.SaveToNode(Node: TDOMNode);
293begin
294 WriteBoolean(Node, 'Explored', Explored);
295 WriteInteger(Node, 'MapCell', MapCell.Id);
296end;
297
298constructor TPlayerCell.Create;
299begin
300 MovesFrom := TUnitMoves.Create;
301 MovesFrom.OwnsObjects := False;
302 MovesTo := TUnitMoves.Create;
303 MovesTo.OwnsObjects := False;
304 Neighbors := TPlayerCells.Create;
305 Neighbors.OwnsObjects := False;
306end;
307
308destructor TPlayerCell.Destroy;
309var
310 I: Integer;
311begin
312 for I := MovesFrom.Count - 1 downto 0 do
313 if TUnitMove(MovesFrom[I]).List.Remove(TUnitMove(MovesFrom[I])) = -1 then
314 raise Exception.Create('MoveFrom cell remove error');
315 FreeAndNil(MovesFrom);
316 for I := MovesTo.Count - 1 downto 0 do
317 if TUnitMove(MovesTo[I]).List.Remove(TUnitMove(MovesTo[I])) = -1 then
318 raise Exception.Create('MoveTo cell remove error');
319 FreeAndNil(MovesTo);
320 for I := Neighbors.Count - 1 downto 0 do
321 if Neighbors[I].Neighbors.Remove(Self) = -1 then
322 raise Exception.Create(SCellRemoveNeighborError);
323 FreeAndNil(Neighbors);
324 inherited;
325end;
326
327{ TPlayerCells }
328
329function TPlayerCells.FindByCellId(Id: Integer): TPlayerCell;
330var
331 I: Integer;
332begin
333 I := 0;
334 while (I < Count) and (Items[I].MapCell.Id <> Id) do Inc(I);
335 if I < Count then Result := Items[I]
336 else Result := nil;
337end;
338
339function TPlayerCells.SearchCell(Cell: TCell): TPlayerCell;
340var
341 I: Integer;
342begin
343 I := 0;
344 while (I < Count) and (Items[I].MapCell <> Cell) do Inc(I);
345 if I < Count then Result := Items[I]
346 else Result := nil;
347end;
348
349procedure TPlayerCells.LoadFromNode(Node: TDOMNode);
350var
351 Node2: TDOMNode;
352 NewCell: TPlayerCell;
353begin
354 Count := 0;
355 Node2 := Node.FirstChild;
356 while Assigned(Node2) and (Node2.NodeName = 'Cell') do begin
357 NewCell := TPlayerCell.Create;
358 NewCell.List := Self;
359 NewCell.LoadFromNode(Node2);
360 Add(NewCell);
361 Node2 := Node2.NextSibling;
362 end;
363end;
364
365procedure TPlayerCells.SaveToNode(Node: TDOMNode);
366var
367 I: Integer;
368 NewNode: TDOMNode;
369begin
370 for I := 0 to Count - 1 do begin;
371 NewNode := Node.OwnerDocument.CreateElement('Cell');
372 Node.AppendChild(NewNode);
373 Items[I].SaveToNode(NewNode);
374 end;
375end;
376
377{ TPlayerMap }
378
379function TPlayerMap.PosToCell(Pos: TPoint): TPlayerCell;
380var
381 I: Integer;
382begin
383 // TODO: Can it be optimized somehow?
384 Result := nil;
385 for I := 0 to Cells.Count - 1 do
386 if Cells[I].MapCell.Terrain <> ttVoid then begin
387 if Cells[I].MapCell.Polygon.IsPointInside(Pos) then begin
388 Result := Cells[I];
389 Exit;
390 end;
391 end;
392end;
393
394function TPlayerMap.CellToPos(Cell: TPlayerCell): TPoint;
395begin
396 Result := Cell.MapCell.PosPx;
397end;
398
399procedure TPlayerMap.LoadFromNode(Node: TDOMNode);
400var
401 NewNode: TDOMNode;
402begin
403 with Node do begin
404 NewNode := FindNode('Cells');
405 if Assigned(NewNode) then
406 Cells.LoadFromNode(NewNode);
407 end;
408end;
409
410procedure TPlayerMap.SaveToNode(Node: TDOMNode);
411var
412 NewNode: TDOMNode;
413begin
414 with Node do begin
415 NewNode := OwnerDocument.CreateElement('Cells');
416 AppendChild(NewNode);
417 Cells.SaveToNode(NewNode);
418 end;
419end;
420
421procedure TPlayerMap.Update;
422var
423 I: Integer;
424 J: Integer;
425 OldCount: Integer;
426begin
427 for I := 0 to Cells.Count - 1 do
428 with Cells[I] do begin
429 for J := Neighbors.Count - 1 downto 0 do
430 DisconnectFrom(Neighbors[J]);
431 end;
432
433 // Update players cells count to map cells count to be 1:1
434 OldCount := Cells.Count;
435 Cells.Count := TGame(Player.Game).Map.Cells.Count;
436 for I := OldCount to Cells.Count - 1 do
437 Cells[I] := TPlayerCell.Create;
438
439 for I := 0 to TGame(Player.Game).Map.Cells.Count - 1 do begin
440 with Cells[I] do begin
441 List := Cells;
442 Explored := False;
443 InVisibleRange := False;
444 MapCell := TGame(Player.Game).Map.Cells[I];
445 TGame(Player.Game).Map.Cells[I].PlayerCell := Cells[I];
446 end;
447 end;
448
449 for I := 0 to Cells.Count - 1 do
450 with TPlayerCell(Cells[I]) do begin
451 for J := 0 to MapCell.Neighbors.Count - 1 do
452 ConnectTo(TPlayerCell(MapCell.Neighbors[J].PlayerCell));
453 end;
454end;
455
456procedure TPlayerMap.Clear;
457begin
458 Cells.Clear;
459end;
460
461constructor TPlayerMap.Create;
462begin
463 Cells := TPlayerCells.Create;
464 Cells.Map := Self;
465end;
466
467destructor TPlayerMap.Destroy;
468begin
469 FreeAndNil(Cells);
470 inherited;
471end;
472
473procedure TPlayerMap.CheckVisibility;
474var
475 I: Integer;
476 C: Integer;
477 NeighCount: Integer;
478begin
479 for I := 0 to Cells.Count - 1 do
480 with Cells[I] do begin
481 NeighCount := 0;
482 for C := 0 to MapCell.Neighbors.Count - 1 do
483 if MapCell.Neighbors[C].Player = Player then
484 Inc(NeighCount);
485
486 InVisibleRange := (NeighCount > 0) or (MapCell.Player = Player);
487 if InVisibleRange and not Explored then Explored := True;
488 end;
489end;
490
491{ TPlayers }
492
493function TPlayers.GetAliveCount: Integer;
494var
495 I: Integer;
496begin
497 Result := 0;
498 for I := 0 to Count - 1 do
499 if Items[I].IsAlive then Inc(Result);
500end;
501
502procedure TPlayers.GetAlivePlayers(Players: TPlayers);
503var
504 I: Integer;
505begin
506 Players.Clear;
507 for I := 0 to Count - 1 do
508 if Items[I].IsAlive then Players.Add(Items[I]);
509end;
510
511function TPlayers.GetPlayerByColor(Color: TColor): TPlayer;
512var
513 I: Integer;
514begin
515 I := 0;
516 while (I < Count) do begin
517 if Assigned(Items[I]) and (Items[I].Color = Color) then Break;
518 Inc(I);
519 end;
520 if I < Count then Result := Items[I]
521 else Result := nil;
522end;
523
524function TPlayers.GetUnusedColor: TColor;
525var
526 I: Integer;
527begin
528 Result := clBlack;
529 for I := 0 to Length(PlayerColors) - 1 do
530 if not Assigned(GetPlayerByColor(PlayerColors[I])) then begin
531 Result := PlayerColors[I];
532 Exit;
533 end;
534end;
535
536procedure TPlayers.Assign(Source: TItemList<TPlayer>);
537var
538 I: Integer;
539begin
540 inherited;
541 for I := 0 to Count - 1 do
542 Items[I].Game := Game;
543end;
544
545procedure TPlayers.New(Name: string; Color: TColor; Mode: TPlayerMode);
546var
547 NewPlayer: TPlayer;
548begin
549 NewPlayer := AddItem(Name);
550 NewPlayer.Color := Color;
551 NewPlayer.Mode := Mode;
552 if Mode = pmComputer then
553 NewPlayer.Agressivity := caMedium;
554end;
555
556function TPlayers.CreateItem(Name: string): TPlayer;
557begin
558 Result := inherited;
559 Result.Game := Game;
560 Result.Mode := pmComputer;
561 Result.Agressivity := caMedium;
562 Result.Color := GetUnusedColor;
563end;
564
565function TPlayers.GetFirstHuman: TPlayer;
566var
567 I: Integer;
568begin
569 I := 0;
570 while (I < Count) and (Items[I].Mode <> pmHuman) do Inc(I);
571 if I < Count then Result := Items[I]
572 else Result := nil;
573end;
574
575procedure TPlayers.LoadConfig(Config: TXmlConfig; Path: string);
576var
577 I: Integer;
578 NewCount: Integer;
579begin
580 with Config do begin
581 NewCount := GetValue(DOMString(Path + '/Count'), -1);
582 NewId := 1;
583 if NewCount >= MinPlayerCount then begin
584 Self.Clear;
585 Count := NewCount;
586 for I := 0 to Count - 1 do begin
587 Items[I] := CreateItem;
588 Items[I].Id := GetNewId;
589 Items[I].LoadConfig(Config, Path + '/Player' + IntToStr(I));
590 end;
591 end;
592 end;
593end;
594
595procedure TPlayers.SaveConfig(Config: TXmlConfig; Path: string);
596var
597 I: Integer;
598begin
599 for I := 0 to Count - 1 do
600 Items[I].SaveConfig(Config, Path + '/Player' + IntToStr(I));
601 with Config do begin
602 SetValue(DOMString(Path + '/Count'), Count);
603 end;
604end;
605
606function TPlayers.GetAlivePlayers: TPlayerArray;
607var
608 I: Integer;
609begin
610 Result := Default(TPlayerArray);
611 SetLength(Result, 0);
612 for I := 0 to Count - 1 do
613 if Items[I].IsAlive then begin
614 SetLength(Result, Length(Result) + 1);
615 Result[Length(Result) - 1] := Items[I];
616 end;
617end;
618
619function TPlayers.GetAlivePlayersWithCities: TPlayerArray;
620var
621 I: Integer;
622begin
623 Result := Default(TPlayerArray);
624 SetLength(Result, 0);
625 for I := 0 to Count - 1 do
626 if Items[I].TotalCities > 0 then begin
627 SetLength(Result, Length(Result) + 1);
628 Result[Length(Result) - 1] := Items[I];
629 end;
630end;
631
632{ TUnitMoves }
633
634procedure TUnitMoves.Assign(Source: TUnitMoves);
635var
636 I: Integer;
637begin
638 while Count > Source.Count do
639 Delete(Count - 1);
640 while Count < Source.Count do
641 Add(TUnitMove.Create);
642 for I := 0 to Count - 1 do begin
643 Items[I].Assign(Source.Items[I]);
644 end;
645end;
646
647function TUnitMoves.SearchByFromTo(CellFrom, CellTo: TPlayerCell): TUnitMove;
648var
649 UnitMove: TUnitMove;
650begin
651 Result := nil;
652 for UnitMove in Self do
653 if (UnitMove.CellFrom = CellFrom) and (UnitMove.CellTo = CellTo) then begin
654 Result := UnitMove;
655 Break;
656 end;
657end;
658
659procedure TUnitMoves.LoadFromNode(Node: TDOMNode);
660var
661 Node2: TDOMNode;
662 NewUnitMove: TUnitMove;
663begin
664 Count := 0;
665 Node2 := Node.FirstChild;
666 while Assigned(Node2) and (Node2.NodeName = 'UnitMove') do begin
667 NewUnitMove := TUnitMove.Create;
668 NewUnitMove.List := Self;
669 NewUnitMove.LoadFromNode(Node2);
670 Add(NewUnitMove);
671 Node2 := Node2.NextSibling;
672 end;
673end;
674
675procedure TUnitMoves.SaveToNode(Node: TDOMNode);
676var
677 I: Integer;
678 NewNode: TDOMNode;
679begin
680 for I := 0 to Count - 1 do begin;
681 NewNode := Node.OwnerDocument.CreateElement('UnitMove');
682 Node.AppendChild(NewNode);
683 Items[I].SaveToNode(NewNode);
684 end;
685end;
686
687{ TUnitMove }
688
689procedure TUnitMove.SetCellFrom(AValue: TPlayerCell);
690var
691 OldValue: TPlayerCell;
692begin
693 if FCellFrom = AValue then Exit;
694 OldValue := FCellFrom;
695 FCellFrom := nil;
696 if Assigned(OldValue) then begin
697 if OldValue.MovesFrom.IndexOf(Self) <> -1 then
698 OldValue.MovesFrom.Remove(Self)
699 else raise Exception.Create('Unit move not found');
700 end;
701 FCellFrom := AValue;
702 if Assigned(FCellFrom) then begin
703 if FCellFrom.MovesFrom.IndexOf(Self) = -1 then
704 FCellFrom.MovesFrom.Add(Self)
705 else raise Exception.Create('Unit move already exists');
706 end else
707end;
708
709procedure TUnitMove.SetCellTo(AValue: TPlayerCell);
710var
711 OldValue: TPlayerCell;
712begin
713 if FCellTo = AValue then Exit;
714 OldValue := FCellTo;
715 FCellTo := nil;
716 if Assigned(OldValue) then begin
717 if OldValue.MovesTo.IndexOf(Self) <> -1 then
718 OldValue.MovesTo.Remove(Self)
719 else raise Exception.Create('Unit move not found');
720 end;
721 FCellTo := AValue;
722 if Assigned(FCellTo) then begin
723 if FCellTo.MovesTo.IndexOf(Self) = -1 then
724 FCellTo.MovesTo.Add(Self)
725 else raise Exception.Create('Unit move already exists');
726 end else
727end;
728
729procedure TUnitMove.Assign(Source: TUnitMove);
730begin
731 CountOnce := Source.CountOnce;
732 CountRepeat := Source.CountRepeat;
733 CellFrom.MapCell.Id := Source.CellFrom.MapCell.Id;
734 CellTo.MapCell.Id := Source.CellTo.MapCell.Id;
735end;
736
737procedure TUnitMove.LoadFromNode(Node: TDOMNode);
738begin
739 CountOnce := ReadInteger(Node, 'CountOnce', 0);
740 CountRepeat := ReadInteger(Node, 'CountRepeat', 0);
741 CellFrom := List.Player.PlayerMap.Cells.FindByCellId(ReadInteger(Node, 'CellFrom', 0));
742 CellTo := List.Player.PlayerMap.Cells.FindByCellId(ReadInteger(Node, 'CellTo', 0));
743end;
744
745procedure TUnitMove.SaveToNode(Node: TDOMNode);
746begin
747 WriteInteger(Node, 'CountOnce', CountOnce);
748 WriteInteger(Node, 'CountRepeat', CountRepeat);
749 WriteInteger(Node, 'CellFrom', CellFrom.MapCell.Id);
750 WriteInteger(Node, 'CellTo', CellTo.MapCell.Id);
751end;
752
753constructor TUnitMove.Create;
754begin
755 List := nil; // Is later set to parent list owning item
756 FCellFrom := nil;
757 FCellTo := nil;
758end;
759
760destructor TUnitMove.Destroy;
761begin
762 CellFrom := nil;
763 CellTo := nil;
764 List := nil;
765 inherited;
766end;
767
768{ TPlayer }
769
770procedure TPlayer.SetGame(AValue: TObject);
771begin
772 if FGame = AValue then Exit;
773 FGame := AValue;
774 Moves.Game := AValue;
775end;
776
777procedure TPlayer.Clear;
778begin
779 TurnStats.Clear;
780 Moves.Clear;
781 PlayerMap.Clear;
782end;
783
784{procedure TPlayer.SetClient(AValue: TClient);
785begin
786 if FClient = AValue then Exit;
787 if Assigned(FClient) then FClient.FControlPlayer := nil;
788 FClient := AValue;
789 if Assigned(FClient) then FClient.FControlPlayer := Self;
790end;
791}
792
793procedure TPlayer.LoadFromNode(Node: TDOMNode);
794var
795 NewNode: TDOMNode;
796begin
797 inherited;
798 StartCell := TGame(FGame).Map.Cells.FindById(ReadInteger(Node, 'StartCell', 0));
799
800 with Node do begin
801 NewNode := FindNode('Map');
802 if Assigned(NewNode) then
803 PlayerMap.LoadFromNode(NewNode);
804 PlayerMap.Update;
805 end;
806 with Node do begin
807 NewNode := FindNode('UnitMoves');
808 if Assigned(NewNode) then
809 Moves.LoadFromNode(NewNode);
810 end;
811 with Node do begin
812 NewNode := FindNode('TurnStats');
813 if Assigned(NewNode) then
814 TurnStats.LoadFromNode(NewNode);
815 end;
816end;
817
818procedure TPlayer.SaveToNode(Node: TDOMNode);
819var
820 NewNode: TDOMNode;
821begin
822 inherited;
823 if Assigned(StartCell) then
824 WriteInteger(Node, 'StartCell', StartCell.Id);
825
826 with Node do begin
827 NewNode := OwnerDocument.CreateElement('Map');
828 AppendChild(NewNode);
829 PlayerMap.SaveToNode(NewNode);
830 end;
831 with Node do begin
832 NewNode := OwnerDocument.CreateElement('UnitMoves');
833 AppendChild(NewNode);
834 Moves.SaveToNode(NewNode);
835 end;
836 with Node do begin
837 NewNode := OwnerDocument.CreateElement('TurnStats');
838 AppendChild(NewNode);
839 TurnStats.SaveToNode(NewNode);
840 end;
841end;
842
843constructor TPlayer.Create;
844begin
845 inherited;
846 Units := TUnits.Create(False);
847 Moves := TUnitMoves.Create;
848 Moves.Player := Self;
849 StartUnits := DefaultPlayerStartUnits;
850 StartCell := nil;
851 PlayerMap := TPlayerMap.Create;
852 PlayerMap.Player := Self;
853 TurnStats := TGameTurnStats.Create;
854end;
855
856destructor TPlayer.Destroy;
857var
858 I: Integer;
859begin
860 //Client := nil;
861 FreeAndNil(TurnStats);
862 FreeAndNil(PlayerMap);
863 FreeAndNil(Moves);
864 for I := Units.Count - 1 downto 0 do
865 Units[I].Player := nil;
866 FreeAndNil(Units);
867 inherited;
868end;
869
870procedure TPlayer.Assign(Source: TItem);
871begin
872 inherited;
873 TotalCells := TPlayer(Source).TotalCells;
874 TotalUnits := TPlayer(Source).TotalUnits;
875 TotalCities := TPlayer(Source).TotalCities;
876 TotalDiscovered := TPlayer(Source).TotalDiscovered;
877 TotalWinObjectiveCells := TPlayer(Source).TotalWinObjectiveCells;
878 StartCell := TPlayer(Source).StartCell;
879 Color := TPlayer(Source).Color;
880 Units.Assign(TPlayer(Source).Units);
881 Moves.Assign(TPlayer(Source).Moves);
882 Nation := TPlayer(Source).Nation;
883end;
884
885procedure TPlayer.LoadConfig(Config: TXmlConfig; Path: string);
886begin
887 with Config do begin
888 Self.Name := string(GetValue(DOMString(Path + '/Name'), ''));
889 Color := TColor(GetValue(DOMString(Path + '/Color'), 0));
890 StartUnits := GetValue(DOMString(Path + '/StartUnits'), 5);
891 Mode := TPlayerMode(GetValue(DOMString(Path + '/Mode'), 0));
892 Defensive := GetValue(DOMString(Path + '/Defensive'), False);
893 Agressivity := TComputerAgressivity(GetValue(DOMString(Path + '/Agressivity'), 0));
894 Nation := TGame(Game).GameSystem.Nations.FindById(GetValue(DOMString(Path + '/Nation'), 0));
895 end;
896end;
897
898procedure TPlayer.SaveConfig(Config: TXmlConfig; Path: string);
899begin
900 with Config do begin
901 SetValue(DOMString(Path + '/Name'), DOMString(Self.Name));
902 SetValue(DOMString(Path + '/Color'), Integer(Color));
903 SetValue(DOMString(Path + '/StartUnits'), StartUnits);
904 SetValue(DOMString(Path + '/Mode'), Integer(Mode));
905 SetValue(DOMString(Path + '/Defensive'), Defensive);
906 SetValue(DOMString(Path + '/Agressivity'), Integer(Agressivity));
907 if Assigned(Nation) then
908 SetValue(DOMString(Path + '/Nation'), Nation.Id)
909 else SetValue(DOMString(Path + '/Nation'), 0)
910 end;
911end;
912
913procedure TPlayer.Attack(var AttackPower, DefendPower: Integer);
914var
915 AttackerDiceCount: Integer;
916 DefenderDiceCount: Integer;
917 S: string;
918 I: Integer;
919 AttackRolls: TList<Integer>;
920 DefendRolls: TList<Integer>;
921begin
922 AttackRolls := TList<Integer>.Create;
923 DefendRolls := TList<Integer>.Create;
924 if AttackPower < 1 then
925 raise Exception.Create(SAttackerPowerPositive);
926 if DefendPower < 0 then
927 raise Exception.Create(SDefenderPowerPositive);
928 while (AttackPower > 0) and (DefendPower > 0) do begin
929 // Risk game rules:
930 // Each side do their dice roll and compare result. Defender wins tie.
931 // Attacker can use three dices and defender two
932 AttackerDiceCount := Min(AttackPower, 3);
933 DefenderDiceCount := Min(DefendPower, 2);
934 // Roll and sort numbers
935 AttackRolls.Count := AttackerDiceCount;
936 for I := 0 to AttackerDiceCount - 1 do begin
937 AttackRolls[I] := Random(7);
938 end;
939 AttackRolls.Sort(TComparer<Integer>.Construct(ComparePointer));
940 S := 'Att:';
941 for I := 0 to AttackerDiceCount - 1 do
942 S := S + IntToStr(Integer(AttackRolls[I])) + ', ';
943 DefendRolls.Count := DefenderDiceCount;
944 for I := 0 to DefenderDiceCount - 1 do begin
945 DefendRolls[I] := Random(7);
946 end;
947 DefendRolls.Sort(TComparer<Integer>.Construct(ComparePointer));
948 S := S + ' Def:';
949 for I := 0 to DefenderDiceCount - 1 do
950 S := S + IntToStr(Integer(DefendRolls[I])) + ', ';
951 // Resolution
952 for I := 0 to Min(AttackerDiceCount, DefenderDiceCount) - 1 do
953 if AttackRolls[I] > DefendRolls[I] then Dec(DefendPower)
954 else Dec(AttackPower);
955 end;
956 FreeAndNil(AttackRolls);
957 FreeAndNil(DefendRolls);
958end;
959
960function CellCompare(constref Item1, Item2: TPlayerCell): Integer;
961var
962 Stack1, Stack2: Integer;
963begin
964 if Assigned(Item1.MapCell.OneUnit) then Stack1 := Item1.MapCell.OneUnit.Power
965 else Stack1 := 0;
966 if Assigned(Item2.MapCell.OneUnit) then Stack2 := Item2.MapCell.OneUnit.Power
967 else Stack2 := 0;
968 if Stack1 > Stack1 then Result := 1
969 else if Stack1 < Stack2 then Result := -1
970 else Result := 0;
971end;
972
973function CellCompareDescending(constref Item1, Item2: TPlayerCell): Integer;
974var
975 Stack1, Stack2: Integer;
976begin
977 if Assigned(Item1.MapCell.OneUnit) then Stack1 := Item1.MapCell.OneUnit.Power
978 else Stack1 := 0;
979 if Assigned(Item2.MapCell.OneUnit) then Stack2 := Item2.MapCell.OneUnit.Power
980 else Stack2 := 0;
981 if Stack1 > Stack2 then Result := -1
982 else if Stack1 < Stack2 then Result := 1
983 else Result := 0;
984end;
985
986procedure TPlayer.MoveUnit(UnitMove: TUnitMove);
987var
988 AttackerPower: Integer;
989 DefenderPower: Integer;
990 UnitCount: Integer;
991begin
992 with UnitMove do begin
993 UnitCount := CountOnce;
994 if CountOnce > CellFrom.MapCell.OneUnit.Power then
995 UnitCount := CellFrom.MapCell.OneUnit.Power;
996 CountOnce := 0;
997 if not Assigned(CellTo.MapCell.OneUnit) then begin
998 CellTo.MapCell.OneUnit := TGame(Game).Units.AddNew(CellFrom.MapCell.OneUnit.Kind, 0);
999 CellTo.MapCell.OneUnit.Player := Self;
1000 CellTo.MapCell.Player := Self;
1001 end;
1002
1003 if CellTo.MapCell.OneUnit.Player = Self then begin
1004 // Inner move
1005 CellTo.MapCell.OneUnit.Power := CellTo.MapCell.OneUnit.Power + UnitCount;
1006 Dec(CellTo.MapCell.OneUnit.Moves);
1007 end else begin
1008 AttackerPower := UnitCount;
1009 if Assigned(CellTo.MapCell.OneUnit) then
1010 DefenderPower := CellTo.MapCell.OneUnit.Power
1011 else DefenderPower := 0;
1012 Attack(AttackerPower, DefenderPower);
1013 if DefenderPower = 0 then begin
1014 // Attacker wins with possible loses
1015 ClearMovesFromCell(CellTo);
1016 CellTo.MapCell.Player := Self;
1017 CellTo.MapCell.OneUnit.Player := Self;
1018 CellTo.MapCell.OneUnit.Power := AttackerPower;
1019 end else
1020 if AttackerPower = 0 then begin
1021 // Defender wins with possible loses
1022 CellTo.MapCell.OneUnit.Power := DefenderPower;
1023 CellTo.MapCell.OneUnit.Moves := CellFrom.MapCell.OneUnit.Moves - 1;
1024 end else
1025 raise Exception.Create(SUnfinishedBattle);
1026 end;
1027 CellFrom.MapCell.OneUnit.Power := CellFrom.MapCell.OneUnit.Power - UnitCount;
1028 CellFrom.MapCell.CheckOwnership;
1029 end;
1030
1031 // Clean from cell from empty units
1032 if UnitMove.CellFrom.MapCell.OneUnit.Power = 0 then begin
1033 UnitMove.CellFrom.MapCell.OneUnit.Player := nil;
1034 UnitMove.CellFrom.MapCell.OneUnit := nil;
1035 end;
1036
1037 // Remove empty move
1038 if (UnitMove.CellFrom.MapCell.Player = Self) and
1039 (UnitMove.CountOnce = 0) and (UnitMove.CountRepeat = 0) then
1040 Moves.Remove(UnitMove);
1041end;
1042
1043procedure TPlayer.MoveAll;
1044var
1045 I: Integer;
1046begin
1047 for I := Moves.Count - 1 downto 0 do
1048 with Moves[I] do
1049 if CountOnce > 0 then begin
1050 if CellFrom.MapCell.Player = Self then begin
1051 MoveUnit(Moves[I]);
1052 end;
1053 end;
1054end;
1055
1056procedure TPlayer.ReduceMovesPower;
1057var
1058 UnitMove: TUnitMove;
1059 Power: Integer;
1060begin
1061 // Power of cell can be reduced by unsucessful enemy attack
1062 for UnitMove in Moves do begin
1063 Power := UnitMove.CellFrom.GetAvialPower;
1064 if Power < 0 then begin
1065 if Abs(Power) < UnitMove.CountOnce then
1066 UnitMove.CountOnce := UnitMove.CountOnce - Abs(Power)
1067 else UnitMove.CountOnce := 0;
1068 end;
1069 end;
1070end;
1071
1072procedure TPlayer.RemoveInvalidMoves;
1073var
1074 I: Integer;
1075begin
1076 for I := Moves.Count - 1 downto 0 do
1077 if Moves[I].CellFrom.MapCell.Player <> Self then
1078 Moves.Delete(I);
1079end;
1080
1081procedure TPlayer.RemoveEmptyUnits;
1082var
1083 I: Integer;
1084 OneUnit: TUnit;
1085begin
1086 for I := 0 to PlayerMap.Cells.Count - 1 do
1087 with PlayerMap.Cells[I] do begin
1088 if Assigned(MapCell.OneUnit) and (MapCell.OneUnit.Power = 0) then begin
1089 OneUnit := MapCell.OneUnit;
1090 MapCell.OneUnit := nil;
1091 OneUnit.Player := nil;
1092 end;
1093 end;
1094end;
1095
1096procedure TPlayer.InitUnitMoves;
1097var
1098 I: Integer;
1099begin
1100 for I := 0 to Units.Count - 1 do
1101 Units[I].Moves := Units[I].Kind.Moves;
1102end;
1103
1104procedure TPlayer.ClearMovesFromCell(Cell: TPlayerCell);
1105var
1106 I: Integer;
1107 OtherPlayerCell: TPlayerCell;
1108begin
1109 if Assigned(Cell.MapCell.Player) then
1110 with TPlayer(Cell.MapCell.Player) do begin
1111 OtherPlayerCell := PlayerMap.Cells.SearchCell(Cell.MapCell);
1112 if Assigned(OtherPlayerCell) then begin
1113 for I := OtherPlayerCell.MovesFrom.Count - 1 downto 0 do
1114 Moves.Remove(OtherPlayerCell.MovesFrom[I]);
1115 end;
1116 end;
1117end;
1118
1119function TPlayer.SetMove(CellFrom, CellTo: TPlayerCell; Power: Integer; Confirmation: Boolean = True): TUnitMove;
1120var
1121 NewMove: TUnitMove;
1122 CountOnce: Integer;
1123 CountRepeat: Integer;
1124 Confirm: Boolean;
1125begin
1126 if CellFrom.MapCell.Player <> Self then
1127 raise Exception.Create('Can''t set move of other player.');
1128 Confirm := True;
1129 Result := Moves.SearchByFromTo(CellFrom, CellTo);
1130
1131 if Assigned(Result) then begin
1132 // Update existing move
1133 CountOnce := Result.CountOnce;
1134 CountRepeat := Result.CountRepeat;
1135 if (Mode = pmHuman) and Confirmation and
1136 Assigned(FOnMove) then
1137 FOnMove(CellFrom, CellTo, CountOnce, CountRepeat, True, Confirm);
1138 end else begin
1139 // Create new move
1140 CountOnce := Power;
1141 CountRepeat := 0;
1142 if (Mode = pmHuman) and Confirmation and
1143 Assigned(FOnMove) then
1144 FOnMove(CellFrom, CellTo, CountOnce, CountRepeat, False, Confirm);
1145 end;
1146 if Confirm then begin
1147 if Assigned(Result) then begin
1148 // Already have such move
1149 if (CountOnce = 0) and (CountRepeat = 0) then begin
1150 Result.List.Remove(Result);
1151 end else begin
1152 Result.CountOnce := CountOnce;
1153 Result.CountRepeat := CountRepeat;
1154 CheckCounterMove(Result);
1155 end;
1156 end else begin
1157 // Add new move
1158 if (CountOnce > 0) or (CountRepeat > 0) then begin
1159 NewMove := TUnitMove.Create;
1160 NewMove.List := Moves;
1161 NewMove.CellFrom := CellFrom;
1162 NewMove.CellTo := CellTo;
1163 NewMove.CountOnce := CountOnce;
1164 NewMove.CountRepeat := CountRepeat;
1165 Moves.Add(NewMove);
1166 Result := NewMove;
1167 CheckCounterMove(NewMove);
1168 if TGame(Game).GameSystem.UnitsMoveImmediately then begin
1169 MoveUnit(NewMove);
1170 end;
1171 end;
1172 end;
1173 if Assigned(TGame(Game).OnMoveUpdated) then
1174 TGame(Game).OnMoveUpdated(Result);
1175 end;
1176end;
1177
1178procedure TPlayer.UpdateRepeatMoves;
1179var
1180 Move: TUnitMove;
1181 AvailPower: Integer;
1182begin
1183 for Move in Moves do
1184 with Move do begin
1185 if CellFrom.MapCell.Player = Self then begin
1186 AvailPower := CellFrom.GetAvialPower + Move.CountOnce;
1187 if CountRepeat <= AvailPower then
1188 CountOnce := CountRepeat
1189 else CountOnce := AvailPower;
1190 end;
1191 end;
1192end;
1193
1194procedure TPlayer.UpdateEmptyCellsNeutral;
1195var
1196 I: Integer;
1197begin
1198 if TGame(Game).GameSystem.EmptyCellsNeutral then
1199 for I := 0 to PlayerMap.Cells.Count - 1 do
1200 with TPlayerCell(PlayerMap.Cells[I]) do begin
1201 MapCell.CheckOwnership;
1202 end;
1203end;
1204
1205procedure TPlayer.Reset;
1206begin
1207 Moves.Clear;
1208 PlayerMap.Cells.Clear;
1209 TotalUnits := 0;
1210 TotalCells := 0;
1211 TotalCities := 0;
1212 TotalDiscovered := 0;
1213 TotalWinObjectiveCells := 0;
1214 TurnStats.Clear;
1215 StartCell := nil;
1216end;
1217
1218procedure TPlayer.Surrender;
1219var
1220 I: Integer;
1221begin
1222 Moves.Clear;
1223 for I := 0 to PlayerMap.Cells.Count - 1 do
1224 if PlayerMap.Cells[I].MapCell.Player = Self then
1225 PlayerMap.Cells[I].MapCell.Player := nil;
1226end;
1227
1228function TPlayer.IsAlive: Boolean;
1229begin
1230 Result := (TotalCells > 0) and Assigned(StartCell);
1231end;
1232
1233procedure TPlayer.CheckCounterMove(Move: TUnitMove);
1234var
1235 CounterMove: TUnitMove;
1236begin
1237 CounterMove := Moves.SearchByFromTo(Move.CellTo, Move.CellFrom);
1238 if Assigned(CounterMove) then begin
1239 // For now, just remove counter move
1240 Moves.Remove(CounterMove);
1241 end;
1242end;
1243
1244procedure TPlayer.SetMode(AValue: TPlayerMode);
1245begin
1246 if FMode = AValue then Exit;
1247 FMode := AValue;
1248end;
1249
1250class function TPlayer.GetFields: TItemFields;
1251var
1252 Field: TItemField;
1253begin
1254 Result := inherited;
1255 Field := Result.AddField(2, 'Nation', SNation, dtReference);
1256 Field := Result.AddField(3, 'Mode', SMode, dtEnumeration);
1257 Field.EnumStates.Add(SHuman);
1258 Field.EnumStates.Add(SComputer);
1259 Result.AddField(4, 'Color', SColor, dtColor);
1260 Field := Result.AddField(5, 'Agressivity', SAgressivity, dtEnumeration);
1261 Field.EnumStates.Add(SLow);
1262 Field.EnumStates.Add(SMedium);
1263 Field.EnumStates.Add(SHigh);
1264 Field.VisibleIfIndex := 2;
1265 Field := Result.AddField(6, 'Defensive', SDefensive, dtBoolean);
1266 Field.VisibleIfIndex := 2;
1267 Result.AddField(7, 'StartUnits', SStartUnits, dtInteger);
1268end;
1269
1270procedure TPlayer.GetValue(Index: Integer; out Value);
1271begin
1272 if Index = 1 then string(Value) := Name
1273 else if Index = 2 then TNation(Value) := Nation
1274 else if Index = 3 then TPlayerMode(Value) := Mode
1275 else if Index = 4 then TColor(Value) := Color
1276 else if Index = 5 then TComputerAgressivity(Value) := Agressivity
1277 else if Index = 6 then Boolean(Value) := Defensive
1278 else if Index = 7 then Integer(Value) := StartUnits
1279 else inherited;
1280end;
1281
1282procedure TPlayer.SetValue(Index: Integer; var Value);
1283begin
1284 if Index = 1 then Name := string(Value)
1285 else if Index = 2 then Nation := TNation(Value)
1286 else if Index = 3 then Mode := TPlayerMode(Value)
1287 else if Index = 4 then Color := TColor(Value)
1288 else if Index = 5 then Agressivity := TComputerAgressivity(Value)
1289 else if Index = 6 then Defensive := Boolean(Value)
1290 else if Index = 7 then StartUnits := Integer(Value)
1291 else inherited;
1292end;
1293
1294function TPlayer.GetReferenceList(Index: Integer): TBaseItemList;
1295begin
1296 if Index = 2 then Result := TGame(Game).GameSystem.Nations.BaseItemList
1297 else Result := nil;
1298end;
1299
1300class function TPlayer.GetClassSysName: string;
1301begin
1302 Result := 'Player';
1303end;
1304
1305class function TPlayer.GetClassName: string;
1306begin
1307 Result := SPlayer;
1308end;
1309
1310function TPlayer.IsAllowedMoveTarget(CellFrom, CellTo: TPlayerCell): Boolean;
1311var
1312 CellToHasOwnUnit: Boolean;
1313 UnitHasMoveEnergy: Boolean;
1314begin
1315 CellToHasOwnUnit := Assigned(CellTo.MapCell.OneUnit) and
1316 (CellTo.MapCell.OneUnit.Player = Self) and (CellTo.MapCell.OneUnit.Power > 0);
1317 UnitHasMoveEnergy := Assigned(CellFrom.MapCell.OneUnit) and
1318 (CellFrom.MapCell.OneUnit.Moves > 0);
1319 Result := UnitHasMoveEnergy and
1320 TGame(Game).Map.IsCellsNeighbor(CellFrom.MapCell, CellTo.MapCell)
1321 and (TGame(Game).GameSystem.UnitsSplitMerge or
1322 (not TGame(Game).GameSystem.UnitsSplitMerge and not CellToHasOwnUnit));
1323end;
1324
1325procedure TPlayer.Grow;
1326var
1327 I: Integer;
1328 Addition: Integer;
1329 Dies: Integer;
1330 NewUnit: TUnit;
1331begin
1332 with TGame(Game).Map do
1333 for I := 0 to Cells.Count - 1 do
1334 with Cells[I] do begin
1335 if (Player = Self) and ((TGame(Game).GrowCells = gcPlayerAll) or
1336 ((TGame(Game).GrowCells = gcPlayerCities) and (Assigned(Building) and (Building.Kind.SpecialType = stCity)))) then begin
1337 if not Assigned(OneUnit) then begin
1338 NewUnit := TGame(Game).Units.AddNew(TGame(Game).GameSystem.UnitKinds.First, 0);
1339 NewUnit.Player := Self;
1340 NewUnit.MapCell := Cells[I];
1341 end;
1342 if OneUnit.Power < MaxPower then begin
1343 // Increase units count
1344 Addition := 0;
1345 if TGame(Game).GrowAmount = gaByOne then begin
1346 Addition := 1;
1347 end else
1348 if TGame(Game).GrowAmount = gaBySquareRoot then begin
1349 Addition := Trunc(Sqrt(OneUnit.Power));
1350 if Addition = 0 then Addition := 1;
1351 end;
1352 if Assigned(Building) and (Building.Kind.BonusGrow > 0) then
1353 OneUnit.Power := OneUnit.Power + Trunc(OneUnit.Power * Building.Kind.BonusGrow / 100);
1354 OneUnit.Power := Min(OneUnit.Power + Addition, MaxPower);
1355 end else
1356 if OneUnit.Power > MaxPower then begin
1357 // Reduce units count
1358 // If cell has more then MaxPower units then additional units dies
1359 // in twice of squeare root of unites over MaxPower
1360 Dies := 2 * Trunc(Sqrt(OneUnit.Power - MaxPower));
1361 OneUnit.Power := Max(OneUnit.Power - Dies, 0);
1362 end;
1363 end;
1364 end;
1365end;
1366
1367end.
1368
Note: See TracBrowser for help on using the repository browser.