source: trunk/Games/UClovece.pas

Last change on this file was 4, checked in by chronos, 3 years ago
  • Added: Human and AI player mode.
  • Added: Human can select one of offered tokens for movement.
File size: 22.5 KB
Line 
1unit UClovece;
2
3{$mode delphi}
4
5interface
6
7uses
8 Classes, SysUtils, Controls, Forms, UGame, fgl, UGraphics, UCanvas;
9
10type
11 TPlayer = class;
12 TTile = class;
13 TTiles = class;
14
15 TTileKind = (tkNone, tkHome, tkYard);
16 TTileKindSet = set of TTileKind;
17
18 { TToken }
19
20 TToken = class
21 private
22 FTile: TTile;
23 procedure SetTile(AValue: TTile);
24 public
25 MoveCount: Integer;
26 Player: TPlayer;
27 Selectable: Boolean;
28 property Tile: TTile read FTile write SetTile;
29 end;
30
31 { TTokens }
32
33 TTokens = class(TFPGObjectList<TToken>)
34 function AddNew(Player: TPlayer): TToken;
35 function GetCountByTileKind(Kind: TTileKindSet): Integer;
36 function GetSingleByTileKind(Kind: TTileKindSet): TToken;
37 procedure GetByTileKind(Kind: TTileKindSet; const Tiles: TTokens);
38 procedure GetSelectableTokens(const Tiles: TTokens);
39 function GetSelectableTokensCount: Integer;
40 end;
41
42 TPlayerMode = (pmHuman, pmAI);
43
44 { TPlayer }
45
46 TPlayer = class
47 Name: string;
48 Color: TColor;
49 Tokens: TTokens;
50 YardTiles: TTiles;
51 StartTile: TTile;
52 TileBeforeHome: TTile;
53 Mode: TPlayerMode;
54 function CanPlay: Boolean;
55 procedure Reset(TokenCount: Integer);
56 constructor Create;
57 destructor Destroy; override;
58 end;
59
60 { TPlayers }
61
62 TPlayers = class(TFPGObjectList<TPlayer>)
63 function AddNew(Name: string; Color: TColor): TPlayer;
64 function CanPlayCount: Integer;
65 end;
66
67 { TTile }
68
69 TTile = class
70 private
71 FOwner: TPlayer;
72 FToken: TToken;
73 procedure SetOwner(AValue: TPlayer);
74 procedure SetToken(AValue: TToken);
75 public
76 Id: Integer;
77 Position: TPoint;
78 Kind: TTileKind;
79 NextTile: TTile;
80 NextTileHome: TTile;
81 AnimateToken: TToken;
82 function GetNext(Steps: Integer; Player: TPlayer): TTile;
83 procedure GetNextSteps(Steps: Integer; Player: TPlayer; Tiles: TTiles);
84 procedure Paint(Canvas: TCanvas);
85 property Owner: TPlayer read FOwner write SetOwner;
86 property Token: TToken read FToken write SetToken;
87 end;
88
89 { TTiles }
90
91 TTiles = class(TFPGObjectList<TTile>)
92 function AddNew(Position: TPoint; Kind: TTileKind; Owner: TPlayer): TTile;
93 function GetTileWithoutToken: TTile;
94 end;
95
96 { TBoard }
97
98 TBoard = class
99 Players: TPlayers;
100 Tiles: TTiles;
101 procedure Build(PlayerCount, TokenCount: Integer);
102 procedure Paint(Canvas: TCanvas);
103 constructor Create;
104 destructor Destroy; override;
105 end;
106
107 TGameState = (gsWaiting, gsDiceThrow, gsTokenSelect, gsGameOver);
108
109 { TClovece }
110
111 TClovece = class(TGame)
112 private
113 MetaCanvas: TMetaCanvas;
114 Processing: Boolean;
115 SelectedToken: TToken;
116 Zoom: Double;
117 Shift1: TPoint;
118 Shift2: TPoint;
119 TokensMoved: Integer;
120 procedure AnimateTokenMovement(Token: TToken; NewTile: TTile);
121 function AISelectToken: TToken;
122 procedure MoveToken(Token: TToken);
123 procedure NextPlayer(SelectableTokenCount: Integer);
124 function GetBoardPos(P: TPoint): TPoint;
125 public
126 Board: TBoard;
127 Players: TPlayers;
128 CurrentPlayer: TPlayer;
129 DiceValue: Integer;
130 LastDiceValue: Integer;
131 DiceThrow: Integer;
132 State: TGameState;
133 PlayerCount: Integer;
134 TokenCount: Integer;
135 procedure ThrowDice;
136 procedure MouseUp(Button: TMouseButton; Position: TPoint); override;
137 procedure KeyUp(Key: Word); override;
138 procedure Reset; override;
139 procedure Paint(Canvas: TCanvas); override;
140 constructor Create; override;
141 destructor Destroy; override;
142 end;
143
144const
145 TileDistance: Integer = 60;
146 TileSize: Integer = 50;
147 TokenSize: Integer = 30;
148 PlayerColors: array[0..7] of TColor = (clRed, clLightBlue, clYellow, clGreen,
149 clFuchsia, clAqua, clBrown, clOrange);
150
151
152implementation
153
154uses
155 UGeometric;
156
157{ TToken }
158
159procedure TToken.SetTile(AValue: TTile);
160var
161 OldTile: TTile;
162begin
163 if FTile = AValue then Exit;
164 if Assigned(FTile) then begin
165 OldTile := FTile;
166 FTile := nil;
167 OldTile.Token := nil;
168 end;
169 FTile := AValue;
170 if Assigned(FTile) then FTile.Token := Self;
171end;
172
173{ TTile }
174
175procedure TTile.SetOwner(AValue: TPlayer);
176begin
177 if FOwner = AValue then Exit;
178 if Assigned(FOwner) then begin
179 if Kind = tkYard then FOwner.YardTiles.Remove(Self);
180 end;
181 FOwner := AValue;
182 if Assigned(FOwner) then begin
183 if Kind = tkYard then FOwner.YardTiles.Add(Self);
184 end;
185end;
186
187procedure TTile.SetToken(AValue: TToken);
188var
189 OldToken: TToken;
190begin
191 if FToken = AValue then Exit;
192 if Assigned(FToken) then begin
193 OldToken := FToken;
194 FToken := nil;
195 OldToken.Tile := nil;
196 end;
197 FToken := AValue;
198 if Assigned(FToken) then FToken.Tile := Self;
199end;
200
201function TTile.GetNext(Steps: Integer; Player: TPlayer): TTile;
202begin
203 // Self
204 Result := nil;
205 if Steps > 0 then begin
206 if Assigned(NextTileHome) and (NextTileHome.Owner = Player) then begin
207 Result := NextTileHome.GetNext(Steps - 1, Player)
208 end else begin
209 if Assigned(NextTile) then Result := NextTile.GetNext(Steps - 1, Player)
210 else begin
211 Result := nil;
212 end;
213 end;
214 end else Result := Self;
215end;
216
217procedure TTile.GetNextSteps(Steps: Integer; Player: TPlayer; Tiles: TTiles);
218begin
219 if Steps > 0 then begin
220 Tiles.Add(Self);
221 if Assigned(NextTileHome) and (NextTileHome.Owner = Player) then begin
222 NextTileHome.GetNextSteps(Steps - 1, Player, Tiles)
223 end else begin
224 if Assigned(NextTile) then NextTile.GetNextSteps(Steps - 1, Player, Tiles);
225 end;
226 end;
227end;
228
229procedure TTile.Paint(Canvas: TCanvas);
230begin
231 Canvas.Pen.Color := clBlack;
232 Canvas.Pen.Width := 5;
233 if Assigned(Owner) then Canvas.Brush.Color := Owner.Color
234 else Canvas.Brush.Color := clWhite;
235 Canvas.Ellipse(
236 Position.X - TileSize div 2,
237 Position.Y - TileSize div 2,
238 Position.X + TileSize div 2,
239 Position.Y + TileSize div 2
240 );
241 if Assigned(Token) then begin
242 Canvas.Pen.Width := 5;
243 Canvas.Brush.Color := Token.Player.Color;
244 Canvas.Ellipse(
245 Position.X - TokenSize div 2,
246 Position.Y - TokenSize div 2,
247 Position.X + TokenSize div 2,
248 Position.Y + TokenSize div 2
249 );
250 if Token.Selectable then begin
251 Canvas.Pen.Color := clBlack;
252 Canvas.Ellipse(
253 Position.X - TokenSize div 2 + 10,
254 Position.Y - TokenSize div 2 + 10,
255 Position.X + TokenSize div 2 - 10,
256 Position.Y + TokenSize div 2 - 10
257 );
258 Canvas.Pen.Color := clBlack;
259 end;
260 end;
261 if Assigned(AnimateToken) then begin
262 Canvas.Pen.Width := 5;
263 Canvas.Brush.Color := AnimateToken.Player.Color;
264 Canvas.Ellipse(
265 Position.X - TokenSize div 2,
266 Position.Y - TokenSize div 2,
267 Position.X + TokenSize div 2,
268 Position.Y + TokenSize div 2
269 );
270 end;
271end;
272
273{ TTiles }
274
275function TTiles.AddNew(Position: TPoint; Kind: TTileKind; Owner: TPlayer
276 ): TTile;
277begin
278 Result := TTile.Create;
279 Result.Id := Count;
280 Result.Position := Position;
281 Result.Kind := Kind;
282 Result.Owner := Owner;
283 Add(Result);
284end;
285
286function TTiles.GetTileWithoutToken: TTile;
287var
288 I: Integer;
289begin
290 I := 0;
291 while (I < Count) and Assigned(Items[I].Token) do Inc(I);
292 if I < Count then Result := Items[I]
293 else Result := nil;
294end;
295
296{ TTokens }
297
298function TTokens.AddNew(Player: TPlayer): TToken;
299begin
300 Result := TToken.Create;
301 Result.Player := Player;
302 Add(Result);
303end;
304
305function TTokens.GetCountByTileKind(Kind: TTileKindSet): Integer;
306var
307 I: Integer;
308begin
309 Result := 0;
310 for I := 0 to Count - 1 do
311 if Items[I].Tile.Kind in Kind then Inc(Result);
312end;
313
314function TTokens.GetSingleByTileKind(Kind: TTileKindSet): TToken;
315var
316 I: Integer;
317begin
318 I := 0;
319 while (I < Count) and (not (Items[I].Tile.Kind in Kind)) do Inc(I);
320 if I < Count then Result := Items[I]
321 else Result := nil;
322end;
323
324procedure TTokens.GetByTileKind(Kind: TTileKindSet; const Tiles: TTokens);
325var
326 I: Integer;
327begin
328 Tiles.Clear;
329 for I := 0 to Count - 1 do
330 if Items[I].Tile.Kind in Kind then Tiles.Add(Items[I]);
331end;
332
333procedure TTokens.GetSelectableTokens(const Tiles: TTokens);
334var
335 I: Integer;
336begin
337 Tiles.Clear;
338 for I := 0 to Count - 1 do
339 if Items[I].Selectable then Tiles.Add(Items[I]);
340end;
341
342function TTokens.GetSelectableTokensCount: Integer;
343var
344 I: Integer;
345begin
346 Result := 0;
347 for I := 0 to Count - 1 do
348 if Items[I].Selectable then Inc(Result);
349end;
350
351{ TPlayers }
352
353function TPlayers.AddNew(Name: string; Color: TColor): TPlayer;
354begin
355 Result := TPlayer.Create;
356 Result.Name := Name;
357 Result.Color := Color;
358 Add(Result);
359end;
360
361function TPlayers.CanPlayCount: Integer;
362var
363 I: Integer;
364begin
365 Result := 0;
366 for I := 0 to Count - 1 do
367 if Items[I].CanPlay then Inc(Result);
368end;
369
370{ TPlayer }
371
372function TPlayer.CanPlay: Boolean;
373begin
374 Result := Tokens.GetCountByTileKind([tkYard, tkNone]) > 0;
375end;
376
377procedure TPlayer.Reset(TokenCount: Integer);
378var
379 I: Integer;
380begin
381 Tokens.Clear;
382 for I := 0 to TokenCount - 1 do
383 Tokens.AddNew(Self);
384 for I := 0 to Tokens.Count - 1 do
385 Tokens[I].Tile := YardTiles[I];
386end;
387
388constructor TPlayer.Create;
389begin
390 Tokens := TTokens.Create;
391 YardTiles := TTiles.Create(False);
392end;
393
394destructor TPlayer.Destroy;
395begin
396 YardTiles.Free;
397 Tokens.Free;
398 inherited;
399end;
400
401{ TBoard }
402
403procedure TBoard.Build(PlayerCount, TokenCount: Integer);
404var
405 I: Integer;
406 J: Integer;
407 Center: TPoint;
408 Angle: Real;
409 AngleDiff: Real;
410 P: TPoint;
411 P1, P2: TPoint;
412 R: Integer;
413 Between: Boolean;
414const
415 BaseR: array[0..8] of Single = (0, 0, 0.5, 0.6, 1, 1.4, 2.7, 3.4, 4.1);
416 DrawBetween: array[0..8] of Boolean = (False, False, True, False, False, False,
417 True, True, True);
418begin
419 R := Trunc(BaseR[PlayerCount] * TileDistance);
420 Between := DrawBetween[PlayerCount];
421 AngleDiff := 2 * Pi / Players.Count;
422 Center := Point(7 * TileDistance, 7 * TileDistance);
423
424 Tiles.Clear;
425 for J := 0 to PlayerCount - 1 do begin
426 Angle := J * AngleDiff;
427 // Normal tiles
428 for I := 1 to TokenCount do begin
429 P := Point(Center.X + R + I * TileDistance, Center.Y - TileDistance);
430 P := RotatePoint(Center, P, Angle);
431 Tiles.AddNew(P, tkNone, nil);
432 if Tiles.Count >= 2 then Tiles[Tiles.Count - 2].NextTile := Tiles[Tiles.Count - 1];
433 end;
434 P := Point(Center.X + R + TokenCount * TileDistance, Center.Y);
435 P := RotatePoint(Center, P, Angle);
436 Tiles.AddNew(P, tkNone, nil);
437 Tiles[Tiles.Count - 2].NextTile := Tiles[Tiles.Count - 1];
438 Players[J].TileBeforeHome := Tiles[Tiles.Count - 1];
439
440 // Start
441 P := Point(Center.X + R + TokenCount * TileDistance, Center.Y + TileDistance);
442 P := RotatePoint(Center, P, Angle);
443 Players[J].StartTile := Tiles.AddNew(P, tkNone, Players[J]);
444 Tiles[Tiles.Count - 2].NextTile := Tiles[Tiles.Count - 1];
445
446 for I := TokenCount - 1 downto 1 do begin
447 P := Point(Center.X + R + I * TileDistance, Center.Y + TileDistance);
448 P := RotatePoint(Center, P, Angle);
449 Tiles.AddNew(P, tkNone, nil);
450 Tiles[Tiles.Count - 2].NextTile := Tiles[Tiles.Count - 1];
451 end;
452 // Between
453 if Between then begin
454 P1 := Point(Center.X + R + TileDistance, Center.Y + TileDistance);
455 P1 := RotatePoint(Center, P1, Angle);
456 P2 := Point(Center.X + R + TileDistance, Center.Y - TileDistance);
457 P2 := RotatePoint(Center, P2, Angle + AngleDiff);
458 P := AveragePoint(P1, P2);
459 end else begin
460 P := Point(Center.X + R, Center.Y + TileDistance);
461 P := RotatePoint(Center, P, Angle);
462 end;
463 Tiles.AddNew(P, tkNone, nil);
464 Tiles[Tiles.Count - 2].NextTile := Tiles[Tiles.Count - 1];
465 end;
466 Tiles[Tiles.Count - 1].NextTile := Tiles[0];
467
468 // Homes
469 for J := 0 to PlayerCount - 1 do begin
470 Angle := J * AngleDiff;
471 for I := TokenCount - 1 downto 0 do begin
472 P := Point(Center.X + R + I * TileDistance, Center.Y);
473 P := RotatePoint(Center, P, Angle);
474 Tiles.AddNew(P, tkHome, Players[J]);
475 if I = TokenCount - 1 then Players[J].TileBeforeHome.NextTileHome := Tiles[Tiles.Count - 1]
476 else Tiles[Tiles.Count - 2].NextTile := Tiles[Tiles.Count - 1];
477 end;
478 end;
479
480 // Yards
481 for J := 0 to PlayerCount - 1 do begin
482 Angle := (J + 0.5) * AngleDiff;
483 for I := 0 to TokenCount - 1 do begin
484 P := Point(Trunc(Center.X + R + TokenCount * TileDistance * 1.35), Center.Y);
485 P := RotatePoint(Center, P, Angle);
486 Tiles.AddNew(RotatePoint(P, Point(P.X + Trunc(0.7 * TileDistance), P.Y), I / TokenCount * 2 * Pi + Pi / 4), tkYard, Players[J]);
487 end;
488 end;
489end;
490
491procedure TBoard.Paint(Canvas: TCanvas);
492var
493 I: Integer;
494begin
495 TileDistance := 60;
496 TileSize := 50;
497 TokenSize := 30;
498 Canvas.Pen.Width := 5;
499 for I := 0 to Tiles.Count - 1 do
500 if Assigned(Tiles[I].NextTile) then begin
501 Canvas.Line(Tiles[I].Position, Tiles[I].NextTile.Position);
502 end;
503 for I := 0 to Tiles.Count - 1 do
504 Tiles[I].Paint(Canvas);
505end;
506
507constructor TBoard.Create;
508begin
509 Tiles := TTiles.Create;
510end;
511
512destructor TBoard.Destroy;
513begin
514 Tiles.Free;
515 inherited;
516end;
517
518{ TClovece }
519
520procedure TClovece.AnimateTokenMovement(Token: TToken; NewTile: TTile);
521var
522 I: Integer;
523 Steps: TTiles;
524begin
525 Steps := TTiles.Create(False);
526 try
527 Token.Tile.GetNextSteps(DiceValue, CurrentPlayer, Steps);
528 Token.Tile := nil;
529 for I := 0 to Steps.Count - 1 do begin
530 Steps[I].AnimateToken := Token;
531 Repaint;
532 Application.ProcessMessages;
533 Sleep(200);
534 Steps[I].AnimateToken := nil;
535 end;
536 finally
537 Steps.Free;
538 end;
539 Token.Tile := NewTile;
540 Token.MoveCount := Token.MoveCount + DiceValue;
541end;
542
543function CompareTokenMoveCount(const Item1, Item2: TToken): Integer;
544begin
545 if Item1.MoveCount < Item2.MoveCount then Result := 1
546 else if Item1.MoveCount > Item2.MoveCount then Result := -1
547 else Result := 0;
548end;
549
550function TClovece.AISelectToken: TToken;
551var
552 Token: TToken;
553 Tokens: TTokens;
554 SortedTokens: TTokens;
555begin
556 Tokens := TTokens.Create(False);
557 CurrentPlayer.Tokens.GetSelectableTokens(Tokens);
558 if Tokens.Count = 0 then begin
559 end else
560 if Tokens.Count = 1 then begin
561 Result := Tokens[0];
562 end else begin
563 if Tokens.GetCountByTileKind([tkYard]) > 0 then begin
564 Result := Tokens.GetSingleByTileKind([tkYard]);
565 end else
566 if Assigned(CurrentPlayer.StartTile.Token) and (CurrentPlayer.StartTile.Token.Player = CurrentPlayer) then begin
567 Result := CurrentPlayer.StartTile.Token
568 end else begin
569 SortedTokens := TTokens.Create(False);
570 Tokens.GetByTileKind([tkNone, tkHome], SortedTokens);
571 Tokens.Sort(CompareTokenMoveCount);
572 Result := Tokens[0];
573 SortedTokens.Free;
574 end;
575 Tokens.Free;
576 end;
577end;
578
579procedure TClovece.MoveToken(Token: TToken);
580var
581 NewTile: TTile;
582begin
583 if Token.Tile.Kind = tkYard then begin
584 // Move from yard
585 if (not Assigned(CurrentPlayer.StartTile.Token)) or (
586 Assigned(CurrentPlayer.StartTile.Token) and (CurrentPlayer.StartTile.Token.Player <> CurrentPlayer)) then begin
587 if Assigned(CurrentPlayer.StartTile.Token) and (CurrentPlayer.StartTile.Token.Player <> CurrentPlayer) then begin
588 // Move opponents token back to its yard
589 CurrentPlayer.StartTile.Token.Tile := CurrentPlayer.StartTile.Token.Player.YardTiles.GetTileWithoutToken;
590 end;
591 // Move one token from yard to start
592 Token.MoveCount := 0;
593 CurrentPlayer.StartTile.Token := Token;
594 end;
595 end else
596 if Token.Tile.Kind in [tkNone, tkHome] then begin
597 NewTile := Token.Tile.GetNext(DiceValue, CurrentPlayer);
598 if Assigned(NewTile) then begin
599 if Assigned(NewTile.Token) then begin
600 if NewTile.Token.Player = Token.Player then begin
601 // Another own token is blocking way, use another one if possible
602 end else begin
603 // Move opponents token back to its yard
604 NewTile.Token.Tile := NewTile.Token.Player.YardTiles.GetTileWithoutToken;
605 AnimateTokenMovement(Token, NewTile);
606 end;
607 end else begin
608 // Normal token move
609 AnimateTokenMovement(Token, NewTile);
610 end;
611 end;
612 end;
613end;
614
615procedure TClovece.NextPlayer(SelectableTokenCount: Integer);
616begin
617 if ((SelectableTokenCount = 0) and (DiceThrow >= 3)) or
618 ((SelectableTokenCount = 0) and (TokensMoved > 0)) or
619 ((SelectableTokenCount > 0) and (DiceValue <> 6)) or
620 (CurrentPlayer.Tokens.GetCountByTileKind([tkHome]) = TokenCount) then begin
621 repeat
622 CurrentPlayer := Players[(Players.IndexOf(CurrentPlayer) + 1) mod Players.Count];
623 until CurrentPlayer.CanPlay or (Players.CanPlayCount >= 2);
624 DiceThrow := 0;
625 DiceValue := -1;
626 TokensMoved := 0;
627 end;
628end;
629
630function TClovece.GetBoardPos(P: TPoint): TPoint;
631begin
632 Result := SubPoint(P, Shift2);
633 Result := Point(Trunc(Result.X / Zoom), Trunc(Result.Y / Zoom));
634 Result := SubPoint(Result, Shift1);
635end;
636
637procedure TClovece.ThrowDice;
638var
639 Token: TToken;
640 NewTile: TTile;
641 I: Integer;
642 SortedTokens: TTokens;
643 SelectableTokenCount: Integer;
644begin
645 if State = gsTokenSelect then begin
646 if Assigned(SelectedToken) then begin
647 SelectableTokenCount := CurrentPlayer.Tokens.GetSelectableTokensCount;
648 for I := 0 to CurrentPlayer.Tokens.Count - 1 do
649 CurrentPlayer.Tokens[I].Selectable := False;
650 MoveToken(SelectedToken);
651 State := gsDiceThrow;
652 Inc(TokensMoved);
653 NextPlayer(SelectableTokenCount);
654 SelectedToken := nil;
655 end;
656 end else
657 if State = gsDiceThrow then begin
658 DiceValue := Random(6) + 1;
659 LastDiceValue := DiceValue;
660 Inc(DiceThrow);
661
662 // Select possible tokens for move
663 SelectableTokenCount := 0;
664 for I := 0 to CurrentPlayer.Tokens.Count - 1 do begin
665 Token := CurrentPlayer.Tokens[I];
666 Token.Selectable := False;
667 if (DiceValue = 6) and (Token.Tile.Kind = tkYard) and (
668 not Assigned(CurrentPlayer.StartTile.Token) or
669 (Assigned(CurrentPlayer.StartTile.Token) and
670 (CurrentPlayer.StartTile.Token.Player <> CurrentPlayer))) then Token.Selectable := True;
671 if Token.Tile.Kind in [tkNone, tkHome] then begin
672 NewTile := Token.Tile.GetNext(DiceValue, CurrentPlayer);
673 if Assigned(NewTile) and
674 ((Assigned(NewTile.Token) and (NewTile.Token.Player <> CurrentPlayer)) or
675 not Assigned(NewTile.Token)) then Token.Selectable := True;
676 end;
677 if Token.Selectable then Inc(SelectableTokenCount);
678 end;
679 if SelectableTokenCount = 0 then begin
680 NextPlayer(SelectableTokenCount);
681 end else begin
682 State := gsTokenSelect;
683 if CurrentPlayer.Mode = pmAI then begin
684 SelectedToken := AISelectToken;
685 ThrowDice;
686 end;
687 end;
688 end;
689{
690 if DiceValue = 6 then begin
691 if CurrentPlayer.Tokens.GetCountByTileKind([tkYard]) > 0 then begin
692 if (not Assigned(CurrentPlayer.StartTile.Token)) or (
693 Assigned(CurrentPlayer.StartTile.Token) and (CurrentPlayer.StartTile.Token.Player <> CurrentPlayer)) then begin
694 if Assigned(CurrentPlayer.StartTile.Token) and (CurrentPlayer.StartTile.Token.Player <> CurrentPlayer) then begin
695 // Move opponents token back to its yard
696 CurrentPlayer.StartTile.Token.Tile := CurrentPlayer.StartTile.Token.Player.YardTiles.GetTileWithoutToken;
697 end;
698 // Move one token from yard to start
699 Token := CurrentPlayer.Tokens.GetSingleByTileKind([tkYard]);
700 Token.MoveCount := 0;
701 CurrentPlayer.StartTile.Token := Token;
702 TurnDone := True;
703 end;
704 end;
705 end;
706 if not TurnDone and (CurrentPlayer.Tokens.GetCountByTileKind([tkNone, tkHome]) > 0) then begin
707 end;
708 Inc(DiceThrow);
709 if ((CurrentPlayer.Tokens.GetCountByTileKind([tkNone]) = 0) and (DiceThrow >= 3)) or
710 ((CurrentPlayer.Tokens.GetCountByTileKind([tkNone]) > 0) and (DiceValue <> 6)) or
711 (CurrentPlayer.Tokens.GetCountByTileKind([tkHome]) = TokenCount) then begin
712 repeat
713 CurrentPlayer := Players[(Players.IndexOf(CurrentPlayer) + 1) mod Players.Count];
714 until CurrentPlayer.CanPlay or (Players.CanPlayCount >= 2);
715 DiceThrow := 0;
716 DiceValue := -1;
717 end;
718 if Players.CanPlayCount < 2 then State := gsGameOver;
719 }
720 Repaint;
721end;
722
723procedure TClovece.MouseUp(Button: TMouseButton; Position: TPoint);
724var
725 I: Integer;
726 Tile: TTile;
727 BoardPos: TPoint;
728begin
729 BoardPos := GetBoardPos(Position);
730 for I := 0 to Board.Tiles.Count - 1 do begin
731 Tile := Board.Tiles[I];
732 if Assigned(Tile.Token) and Tile.Token.Selectable and
733 (Distance(Tile.Position, BoardPos) < (TileSize div 2)) then begin
734 SelectedToken := Board.Tiles[I].Token;
735 Break;
736 end;
737 end;
738 if (State in [gsDiceThrow, gsTokenSelect]) and not Processing then begin
739 Processing := True;
740 ThrowDice;
741 Processing := False;
742 end;
743 Repaint;
744end;
745
746procedure TClovece.KeyUp(Key: Word);
747const
748 KeyQ = 81;
749 KeyW = 87;
750begin
751 if Key = KeyQ then begin
752 if PlayerCount < 8 then Inc(PlayerCount);
753 Reset;
754 end;
755 if Key = KeyW then begin
756 if PlayerCount > 2 then Dec(PlayerCount);
757 Reset;
758 end;
759end;
760
761procedure TClovece.Reset;
762var
763 I: Integer;
764 Player: TPlayer;
765begin
766 Players.Clear;
767 for I := 0 to PlayerCount - 1 do begin
768 Player := Players.AddNew('Player ' + IntToStr(I + 1), PlayerColors[I]);
769 //if I > 0 then Player.Mode := pmAI else Player.Mode := pmHuman;
770 end;
771 Board.Build(Players.Count, TokenCount);
772 for I := 0 to Players.Count - 1 do begin
773 Players[I].Reset(TokenCount);
774 end;
775 CurrentPlayer := Players.First;
776 DiceValue := -1;
777 DiceThrow := 0;
778 State := gsDiceThrow;
779 Repaint;
780end;
781
782procedure TClovece.Paint(Canvas: TCanvas);
783var
784 R: TRect;
785 WidthFactor: Double;
786 HeightFactor: Double;
787begin
788 with Canvas do begin
789 Pen.Color := clBlack;
790 Brush.Opacity := $ff;
791 Brush.Color := clGray;
792 Brush.Style := bsSolid;
793 Rectangle(Rect(0, 0, Size.Width, Size.Height));
794
795 MetaCanvas.Reset;
796 Board.Paint(MetaCanvas);
797 R := MetaCanvas.GetRealRect;
798 Shift1 := Point(-R.Left, -R.Top);
799 MetaCanvas.Move(Shift1);
800 // Scale to window size
801 WidthFactor := Size.Width / R.Width / 1.05;
802 HeightFactor := Size.Height / R.Height / 1.05;
803 if WidthFactor > HeightFactor then Zoom := HeightFactor
804 else Zoom := WidthFactor;
805 MetaCanvas.Zoom(Zoom);
806 // Center to window
807 R := MetaCanvas.GetRealRect;
808 Shift2 := Point((Size.Width - R.Width) div 2, (Size.Height - R.Height) div 2);
809 MetaCanvas.Move(Shift2);
810 MetaCanvas.DrawTo(Canvas);
811
812 Brush.Opacity := 0;
813 Pen.Color := clBlack;
814 Brush.Style := bsSolid;
815 Brush.Color := CurrentPlayer.Color;
816 TextOut(Point(0, 0), 'Player: ' + CurrentPlayer.Name);
817 Brush.Style := bsClear;
818 TextOut(Point(0, 30), 'Dice: ' + IntToStr(DiceValue) + ' ' + IntToStr(LastDiceValue));
819 TextOut(Point(0, 60), 'Throw: ' + IntToStr(DiceThrow));
820 end;
821end;
822
823constructor TClovece.Create;
824begin
825 inherited;
826 MetaCanvas := TMetaCanvas.Create;
827 Players := TPlayers.Create;
828 Board := TBoard.Create;
829 Board.Players := Players;
830 PlayerCount := 4;
831 TokenCount := 4;
832end;
833
834destructor TClovece.Destroy;
835begin
836 Board.Free;
837 Players.Free;
838 MetaCanvas.Free;
839 inherited;
840end;
841
842end.
843
Note: See TracBrowser for help on using the repository browser.