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