source: trunk/UGame.pas

Last change on this file was 84, checked in by chronos, 3 years ago
  • Fixed: Use clear background color under score text instead of solid.
  • Fixed: Missing snap package x11 plug.
  • Fixed: Snap package was not able to use language files.
  • Modified: Build snap using lxd instead of multipass.
File size: 37.3 KB
Line 
1unit UGame;
2
3{$mode delphi}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, Dialogs, fgl, Graphics, Types, Forms, Math, DateUtils,
9 Controls, URegistry;
10
11type
12 TGame = class;
13 TMoveDirection = (drNone, drLeft, drUp, drRight, drDown);
14 TTileAction = (taNone, taMove, taMerge, taAppear);
15
16 { TTile }
17
18 TTile = class
19 Index: TPoint;
20 Value: Integer;
21 NewValue: Integer;
22 Merged: Boolean;
23 Action: TTileAction;
24 Shift: TPoint;
25 procedure Assign(Source: TTile);
26 end;
27
28 TTiles = class(TFPGObjectList<TTile>)
29 end;
30
31 { TArea }
32
33 TArea = record
34 P1, P2: TPoint;
35 function Increment: TPoint;
36 class function Create(P1, P2: TPoint): TArea; static; overload;
37 class function Create(X1, Y1, X2, Y2: Integer): TArea; static; overload;
38 end;
39
40 { THistoryMove }
41
42 THistoryMove = class
43 Direction: TMoveDirection;
44 NewItemPos: TPoint;
45 NewItemValue: Integer;
46 procedure SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
47 procedure LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
48 end;
49
50 { THistoryMoves }
51
52 THistoryMoves = class(TFPGObjectList<THistoryMove>)
53 procedure SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
54 procedure LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
55 end;
56
57 TTilePosValue = record
58 Pos: TPoint;
59 Value: Integer;
60 end;
61
62 { THistory }
63
64 THistory = class
65 Game: TGame;
66 Moves: THistoryMoves;
67 InitialTiles: array of TTilePosValue;
68 procedure GetStep(GameStep: TGame; Step: Integer);
69 procedure Clear;
70 constructor Create;
71 destructor Destroy; override;
72 procedure SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
73 procedure LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
74 end;
75
76 { TBoard }
77
78 TBoard = class
79 private
80 FSize: TPoint;
81 procedure SetSize(AValue: TPoint);
82 public
83 Tiles: array of array of TTile;
84 procedure Assign(Source: TBoard);
85 procedure Clear;
86 procedure ClearMerged;
87 function GetValueSum: Integer;
88 function GetHighestTileValue: Integer;
89 function GetEmptyTilesCount: Integer;
90 procedure GetEmptyTiles(EmptyTiles: TTiles);
91 procedure SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
92 procedure LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
93 destructor Destroy; override;
94 property Size: TPoint read FSize write SetSize;
95 end;
96
97 TTileSkin = (tsLinear, tsPowerOfTwo, tsAlpha, tsRoman, tsBinary);
98
99 { TGame }
100
101 TGame = class
102 private
103 FMoving: Boolean;
104 FOnChange: TNotifyEvent;
105 FOnGameOver: TNotifyEvent;
106 FOnPaint: TNotifyEvent;
107 FOnWin: TNotifyEvent;
108 FRecordHistory: Boolean;
109 FRunning: Boolean;
110 FScore: Integer;
111 FCanUndo: Boolean;
112 FBoardUndo: TBoard;
113 FSkin: TTileSkin;
114 function GetTileColor(Value: Integer): TColor;
115 procedure SetRecordHistory(AValue: Boolean);
116 procedure SetScore(AValue: Integer);
117 procedure DoChange;
118 procedure DoPaint;
119 procedure RenderTile(Canvas: TCanvas; Tile: TTile; TileRect: TRect; WithText: Boolean);
120 procedure GameOver;
121 procedure SetSkin(AValue: TTileSkin);
122 procedure Win;
123 function FillRandomTile: TTile;
124 function GetMoveArea(Direction: TMoveDirection): TArea;
125 procedure MoveAllAnimate(Direction: TMoveDirection);
126 function CanMergeTile(Value1, Value2: Integer): Boolean;
127 function MergeTile(Value1, Value2: Integer): Integer;
128 procedure AnimateTiles;
129 public
130 Board: TBoard;
131 TopScore: Integer;
132 AnimationDuration: Integer;
133 AnimationTick: Integer;
134 WinTileValue: Integer;
135 UndoEnabled: Boolean;
136 History: THistory;
137 BackgroundColor: TColor;
138 Value2Chance: Double;
139 function CanUndo: Boolean;
140 procedure Undo;
141 function CanMergeDirection(Direction: TMoveDirection): Boolean;
142 function CanMoveDirection(Direction: TMoveDirection): Boolean;
143 function CanMove: Boolean;
144 procedure Assign(Source: TGame);
145 procedure New;
146 procedure Render(Canvas: TCanvas; CanvasSize: TPoint);
147 procedure MoveAll(Direction: TMoveDirection; Animation: Boolean);
148 procedure MoveAllAndUpdate(Direction: TMoveDirection; Animation: Boolean);
149 procedure MoveTile(SourceTile, TargetTile: TTile);
150 function IsValidPos(Pos: TPoint): Boolean;
151 procedure SaveToRegistry(RegContext: TRegistryContext);
152 procedure LoadFromRegistry(RegContext: TRegistryContext);
153 function GetTileSkinValue(Value: Integer): string;
154 function GetTileSkinScore(Value: Integer): Integer;
155 constructor Create;
156 destructor Destroy; override;
157 property Score: Integer read FScore write SetScore;
158 property Running: Boolean read FRunning write FRunning;
159 property OnChange: TNotifyEvent read FOnChange write FOnChange;
160 property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
161 property OnWin: TNotifyEvent read FOnWin write FOnWin;
162 property OnGameOver: TNotifyEvent read FOnGameOver write FOnGameOver;
163 property Moving: Boolean read FMoving;
164 property RecordHistory: Boolean read FRecordHistory write SetRecordHistory;
165 property Skin: TTileSkin read FSkin write SetSkin;
166 end;
167
168 TGames = class(TFPGObjectList<TGame>)
169 end;
170
171var
172 SkinText: array[TTileSkin] of string;
173
174const
175 DirectionDiff: array[TMoveDirection] of TPoint = (
176 (X: 0; Y: 0), (X: -1; Y: 0), (X: 0; Y: -1), (X: 1; Y: 0), (X: 0; Y: 1)
177 );
178 DirectionText: array[TMoveDirection] of string = ('None', 'Left', 'Up', 'Right', 'Down');
179 InitialTileCount = 2;
180
181resourcestring
182 SScore = 'Score';
183 STopScore = 'Top score';
184 SSkinLinear = 'Linear';
185 SSkinPowerOfTwo = 'Power of two';
186 SSkinAlpha = 'Alpha';
187 SSkinRoman = 'Roman';
188 SSkinBinary = 'Binary';
189 STileShouldBeEmpty = 'Tile should be empty';
190
191
192procedure Translate;
193
194
195implementation
196
197uses
198 UCore, UMetaCanvas;
199
200procedure Translate;
201begin
202 SkinText[tsLinear] := SSkinLinear;
203 SkinText[tsPowerOfTwo] := SSkinPowerOfTwo;
204 SkinText[tsAlpha] := SSkinAlpha;
205 SkinText[tsRoman] := SSkinRoman;
206 SkinText[tsBinary] := SSkinBinary;
207end;
208
209{ THistoryMoves }
210
211procedure THistoryMoves.SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
212var
213 I: Integer;
214begin
215 with Reg do begin
216 CurrentContext := RegContext;
217 WriteInteger('Count', Count);
218 for I := 0 to Count - 1 do begin
219 Items[I].SaveToRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\' + IntToStr(I)));
220 end;
221 end;
222end;
223
224procedure THistoryMoves.LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
225var
226 I: Integer;
227begin
228 with Reg do begin
229 CurrentContext := RegContext;
230 Count := ReadIntegerWithDefault('Count', 0);
231 for I := 0 to Count - 1 do begin
232 Items[I] := THistoryMove.Create;
233 THistoryMove(Items[I]).LoadFromRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\' + IntToStr(I)));
234 end;
235 end;
236end;
237
238{ THistoryMove }
239
240procedure THistoryMove.SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
241begin
242 with Reg do begin
243 CurrentContext := RegContext;
244 WriteInteger('Direction', Integer(Direction));
245 WriteInteger('NewItemPosX', NewItemPos.X);
246 WriteInteger('NewItemPosY', NewItemPos.Y);
247 WriteInteger('NewItemValue', NewItemValue);
248 end;
249end;
250
251procedure THistoryMove.LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
252begin
253 with Reg do begin
254 CurrentContext := RegContext;
255 Direction := TMoveDirection(ReadIntegerWithDefault('Direction', Integer(drNone)));
256 NewItemPos := Point(ReadIntegerWithDefault('NewItemPosX', 0),
257 ReadIntegerWithDefault('NewItemPosY', 0));
258 NewItemValue := ReadIntegerWithDefault('NewItemValue', 0);
259 end;
260end;
261
262{ THistory }
263
264procedure THistory.GetStep(GameStep: TGame; Step: Integer);
265var
266 I: Integer;
267begin
268 GameStep.Board.Size := Game.Board.Size;
269 GameStep.Board.Clear;
270 GameStep.Skin := Game.Skin;
271 GameStep.Score := 0;
272 for I := 0 to Length(InitialTiles) - 1 do
273 GameStep.Board.Tiles[InitialTiles[I].Pos.Y, InitialTiles[I].Pos.X].Value := InitialTiles[I].Value;
274 for I := 0 to Step - 1 do
275 with Moves[I] do begin
276 GameStep.MoveAll(Direction, False);
277 if GameStep.Board.Tiles[NewItemPos.Y, NewItemPos.X].Value = 0 then
278 GameStep.Board.Tiles[NewItemPos.Y, NewItemPos.X].Value := NewItemValue
279 else raise Exception.Create(STileShouldBeEmpty);
280 end;
281end;
282
283procedure THistory.Clear;
284begin
285 Moves.Clear;
286 SetLength(InitialTiles, 0);
287end;
288
289constructor THistory.Create;
290begin
291 Moves := THistoryMoves.Create;
292end;
293
294destructor THistory.Destroy;
295begin
296 FreeAndNil(Moves);
297 inherited Destroy;
298end;
299
300procedure THistory.SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
301var
302 I: Integer;
303begin
304 with Reg do begin
305 CurrentContext := TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\InitialTilesPos');
306 WriteInteger('Count', Length(InitialTiles));
307 for I := 0 to Length(InitialTiles) - 1 do begin
308 WriteInteger('X' + IntToStr(I), InitialTiles[I].Pos.X);
309 WriteInteger('Y' + IntToStr(I), InitialTiles[I].Pos.Y);
310 WriteInteger('Value' + IntToStr(I), InitialTiles[I].Value);
311 end;
312 Moves.SaveToRegistry(Reg, RegContext);
313 end;
314end;
315
316procedure THistory.LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
317var
318 I: Integer;
319begin
320 with Reg do begin
321 CurrentContext := TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\InitialTilesPos');
322 SetLength(InitialTiles, ReadIntegerWithDefault('Count', 0));
323 for I := 0 to Length(InitialTiles) - 1 do begin
324 InitialTiles[I].Pos := Point(ReadIntegerWithDefault('X' + IntToStr(I), 0),
325 ReadIntegerWithDefault('Y' + IntToStr(I), 0));
326 InitialTiles[I].Value := ReadIntegerWithDefault('Value' + IntToStr(I), 0);
327 end;
328 end;
329 Moves.LoadFromRegistry(Reg, RegContext);
330end;
331
332{ TArea }
333
334function TArea.Increment: TPoint;
335begin
336 Result := Point(Sign(P2.X - P1.X), Sign(P2.Y - P1.Y));
337 if Result.X = 0 then Result.X := 1;
338 if Result.Y = 0 then Result.Y := 1;
339end;
340
341class function TArea.Create(P1, P2: TPoint): TArea;
342begin
343 Result.P1 := P1;
344 Result.P2 := P2;
345end;
346
347class function TArea.Create(X1, Y1, X2, Y2: Integer): TArea;
348begin
349 Result.P1 := Point(X1, Y1);
350 Result.P2 := Point(X2, Y2);
351end;
352
353{ TBoard }
354
355procedure TBoard.SetSize(AValue: TPoint);
356var
357 X, Y: Integer;
358begin
359 if FSize = AValue then Exit;
360 for Y := 0 to FSize.Y - 1 do
361 for X := 0 to FSize.X - 1 do
362 Tiles[Y, X].Free;
363 FSize := AValue;
364 SetLength(Tiles, FSize.Y, FSize.X);
365 for Y := 0 to FSize.Y - 1 do
366 for X := 0 to FSize.X - 1 do begin
367 Tiles[Y, X] := TTile.Create;
368 Tiles[Y, X].Index := Point(X, Y);
369 end;
370end;
371
372procedure TBoard.Assign(Source: TBoard);
373var
374 X, Y: Integer;
375begin
376 Size := Source.Size;
377 for Y := 0 to Size.Y - 1 do
378 for X := 0 to Size.X - 1 do
379 Tiles[Y, X].Assign(Source.Tiles[Y, X]);
380end;
381
382procedure TBoard.GetEmptyTiles(EmptyTiles: TTiles);
383var
384 X, Y: Integer;
385begin
386 EmptyTiles.Clear;
387 for Y := 0 to Size.Y - 1 do
388 for X := 0 to Size.X - 1 do
389 if Tiles[Y, X].Value = 0 then
390 EmptyTiles.Add(Tiles[Y, X]);
391end;
392
393procedure TBoard.SaveToRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
394var
395 X, Y: Integer;
396 Value: string;
397begin
398 with Reg do begin
399 CurrentContext := RegContext;
400
401 WriteInteger('SizeX', Size.X);
402 WriteInteger('SizeY', Size.Y);
403 Value := '';
404 for Y := 0 to Size.Y - 1 do begin
405 for X := 0 to Size.X - 1 do begin
406 Value := Value + IntToStr(Tiles[Y, X].Value);
407 if X < Size.X - 1 then Value := Value + ',';
408 end;
409 if Y < Size.Y - 1 then Value := Value + ';'
410 end;
411 WriteString('TileValues', Value);
412 end;
413end;
414
415procedure TBoard.LoadFromRegistry(Reg: TRegistryEx; RegContext: TRegistryContext);
416var
417 X, Y: Integer;
418 Items: TStringList;
419 Lines: TStringList;
420 Number: Integer;
421begin
422 with Reg do begin
423 CurrentContext := RegContext;
424
425 Size := Point(ReadIntegerWithDefault('SizeX', 4), ReadIntegerWithDefault('SizeY', 4));
426 Items := TStringList.Create;
427 Items.Delimiter := ',';
428 Lines := TStringList.Create;
429 Lines.Delimiter := ';';
430 Lines.DelimitedText := ReadStringWithDefault('TileValues', '');
431 for Y := 0 to Lines.Count - 1 do begin
432 Items.DelimitedText := Lines[Y];
433 for X := 0 to Items.Count - 1 do begin
434 if TryStrToInt(Items[X], Number) and (X < Size.X) and (Y < Size.Y) then
435 Tiles[Y, X].Value := Number;
436 end;
437 end;
438 Lines.Free;
439 Items.Free;
440 end;
441end;
442
443destructor TBoard.Destroy;
444begin
445 Size := Point(0, 0);
446 inherited Destroy;
447end;
448
449procedure TBoard.ClearMerged;
450var
451 X, Y: Integer;
452begin
453 for Y := 0 to Size.Y - 1 do
454 for X := 0 to Size.X - 1 do
455 Tiles[Y, X].Merged := False;
456end;
457
458function TBoard.GetValueSum: Integer;
459var
460 X, Y: Integer;
461begin
462 Result := 0;
463 for Y := 0 to Size.Y - 1 do
464 for X := 0 to Size.X - 1 do
465 Inc(Result, Tiles[Y, X].Value);
466end;
467
468function TBoard.GetEmptyTilesCount: Integer;
469var
470 X, Y: Integer;
471begin
472 Result := 0;
473 for Y := 0 to Size.Y - 1 do
474 for X := 0 to Size.X - 1 do
475 if Tiles[Y, X].Value = 0 then
476 Inc(Result);
477end;
478
479function TBoard.GetHighestTileValue: Integer;
480var
481 X, Y: Integer;
482begin
483 Result := 0;
484 for Y := 0 to Size.Y - 1 do
485 for X := 0 to Size.X - 1 do
486 if Result < Tiles[Y, X].Value then Result := Tiles[Y, X].Value;
487end;
488
489procedure TBoard.Clear;
490var
491 X, Y: Integer;
492begin
493 for Y := 0 to Size.Y - 1 do
494 for X := 0 to Size.X - 1 do
495 Tiles[Y, X].Value := 0;
496end;
497
498
499{ TTile }
500
501procedure TTile.Assign(Source: TTile);
502begin
503 Value := Source.Value;
504 Merged := Source.Merged;
505end;
506
507{ TGame }
508
509procedure TGame.DoChange;
510begin
511 if Assigned(FOnChange) then FOnChange(Self);
512end;
513
514procedure TGame.DoPaint;
515begin
516 if Assigned(FOnPaint) then FOnPaint(Self);
517end;
518
519procedure TGame.GameOver;
520begin
521 if Running and Assigned(FOnGameOver) then FOnGameOver(Self);
522 Running := False;
523end;
524
525procedure TGame.SetSkin(AValue: TTileSkin);
526begin
527 if FSkin = AValue then Exit;
528 FSkin := AValue;
529 DoPaint;
530end;
531
532procedure TGame.Win;
533begin
534 if Assigned(FOnWin) then FOnWin(Self);
535end;
536
537function TGame.FillRandomTile: TTile;
538var
539 EmptyTiles: TTiles;
540 NewValue: Integer;
541begin
542 Result := nil;
543 EmptyTiles := TTiles.Create(False);
544 Board.GetEmptyTiles(EmptyTiles);
545 if EmptyTiles.Count > 0 then begin
546 if Random < Value2Chance then NewValue := 2 else NewValue := 1;
547 Result := EmptyTiles[Random(EmptyTiles.Count)];
548 Result.Value := NewValue;
549 Result.Action := taAppear;
550 end;
551 EmptyTiles.Free;
552end;
553
554function TGame.GetMoveArea(Direction: TMoveDirection): TArea;
555begin
556 case Direction of
557 drNone: Result := TArea.Create(0, 0, 0, 0);
558 drLeft: Result := TArea.Create(1, 0, Board.Size.X - 1, Board.Size.Y - 1);
559 drUp: Result := TArea.Create(0, 1, Board.Size.X - 1, Board.Size.Y - 1);
560 drRight: Result := TArea.Create(Board.Size.X - 2, 0, 0, Board.Size.Y - 1);
561 drDown: Result := TArea.Create(0, Board.Size.Y - 2, Board.Size.X - 1, 0);
562 end;
563end;
564
565function TGame.CanMove: Boolean;
566begin
567 Result := CanMoveDirection(drLeft) or CanMoveDirection(drRight) or
568 CanMoveDirection(drUp) or CanMoveDirection(drDown);
569end;
570
571procedure TGame.Assign(Source: TGame);
572begin
573 Board.Assign(Source.Board);
574 FBoardUndo.Assign(Source.FBoardUndo);
575 FCanUndo := Source.FCanUndo;
576 TopScore := Source.TopScore;
577 AnimationDuration := Source.AnimationDuration;
578 WinTileValue := Source.WinTileValue;
579 UndoEnabled := Source.UndoEnabled;
580 FScore := Source.FScore;
581 FRunning := Source.FRunning;
582 Skin := Source.Skin;
583end;
584
585procedure TGame.New;
586var
587 I: Integer;
588 Tile: TTile;
589begin
590 FCanUndo := False;
591 Board.Clear;
592 Score := 0;
593 Running := True;
594 History.Clear;
595 if RecordHistory then begin
596 for I := 0 to InitialTileCount - 1 do begin
597 SetLength(History.InitialTiles, Length(History.InitialTiles) + 1);
598 Tile := FillRandomTile;
599 History.InitialTiles[Length(History.InitialTiles) - 1].Pos := Tile.Index;
600 History.InitialTiles[Length(History.InitialTiles) - 1].Value := Tile.Value;
601 end;
602 end else begin
603 for I := 0 to InitialTileCount - 1 do
604 FillRandomTile;
605 end;
606 AnimateTiles;
607 DoChange;
608 DoPaint;
609end;
610
611procedure TGame.Render(Canvas: TCanvas; CanvasSize: TPoint);
612var
613 X, Y: Integer;
614 TileSize: TPoint;
615 ValueStr: string;
616 Frame: TRect;
617 TileRect: TRect;
618 TopBarHeight: Integer;
619 TileMargin: Integer;
620 TileCenter: TPoint;
621 S: TPoint;
622 MetaCanvas: TMetaCanvas;
623begin
624 MetaCanvas := TMetaCanvas.Create;
625 MetaCanvas.Size := Point(Canvas.Width, Canvas.Height);
626
627 TopBarHeight := ScaleY(24, 96);
628 MetaCanvas.Brush.Style := bsSolid;
629 MetaCanvas.Brush.Color := Core.ThemeManager1.Theme.ColorControl;
630 MetaCanvas.FillRect(0, 0, MetaCanvas.Width, MetaCanvas.Height);
631
632 ValueStr := SScore + ': ' + IntToStr(Score);
633 MetaCanvas.Brush.Style := bsClear;
634 MetaCanvas.Font.Color := Core.ThemeManager1.Theme.ColorControlText;
635 MetaCanvas.Font.Height := Trunc(TopBarHeight * 0.7);
636 MetaCanvas.TextOut(ScaleY(16, 96), (TopBarHeight - MetaCanvas.TextHeight(ValueStr)) div 2, ValueStr);
637
638 ValueStr := STopScore + ': ' + IntToStr(TopScore);
639 MetaCanvas.Font.Color := Core.ThemeManager1.Theme.ColorControlText;
640 MetaCanvas.Font.Height := Trunc(TopBarHeight * 0.7);
641 MetaCanvas.TextOut(ScaleY(136, 96), (TopBarHeight - MetaCanvas.TextHeight(ValueStr)) div 2, ValueStr);
642
643 // Form.Canvas.Width and Form.Canvas.Height is not working correctly under Windows.
644 // So dimensions are provided by CanvasSize parameter.
645 Frame := Rect(2, TopBarHeight, CanvasSize.X - 2, CanvasSize.Y - 2);
646 TileSize := Point(Frame.Width div Board.Size.X, Frame.Height div Board.Size.Y);
647 if TileSize.X < TileSize.Y then TileSize.Y := TileSize.X;
648 if TileSize.Y < TileSize.X then TileSize.X := TileSize.Y;
649 Frame := Rect(Frame.Width div 2 - (Board.Size.X * TileSize.X) div 2,
650 Frame.Top + Frame.Height div 2 - (Board.Size.Y * TileSize.Y) div 2,
651 Frame.Width div 2 + (Board.Size.X * TileSize.X) div 2,
652 Frame.Top + Frame.Height div 2 + (Board.Size.Y * TileSize.Y) div 2);
653
654 TileMargin := Round(Frame.Width / Board.Size.X * 0.03);
655
656 MetaCanvas.Brush.Style := bsSolid;
657 MetaCanvas.Brush.Color := clGray;
658 MetaCanvas.Pen.Style := psClear;
659 MetaCanvas.RoundRect(Frame, ScaleX(Frame.Width div (20 * Board.Size.X), 96),
660 ScaleY(Frame.Height div (20 * Board.Size.Y), 96));
661
662 Frame := Rect(Frame.Left + TileMargin, Frame.Top + TileMargin,
663 Frame.Right - TileMargin, Frame.Bottom - TileMargin);
664 TileSize := Point(Frame.Width div Board.Size.X, Frame.Height div Board.Size.Y);
665
666 MetaCanvas.Font.Color := clBlack;
667
668 // Draw static tiles
669 for Y := 0 to Board.Size.Y - 1 do
670 for X := 0 to Board.Size.X - 1 do begin
671 if (Board.Tiles[Y, X].Action <> taNone) then MetaCanvas.Brush.Color := GetTileColor(0)
672 else MetaCanvas.Brush.Color := GetTileColor(Board.Tiles[Y, X].Value);
673 MetaCanvas.Brush.Style := bsSolid;
674 TileRect := Bounds(
675 Frame.Left + X * TileSize.X + TileMargin,
676 Frame.Top + Y * TileSize.Y + TileMargin,
677 TileSize.X - 2 * TileMargin, TileSize.Y - 2 * TileMargin);
678 RenderTile(MetaCanvas, Board.Tiles[Y, X], TileRect, Board.Tiles[Y, X].Action = taNone);
679 end;
680
681 // Draw moving tiles
682 for Y := 0 to Board.Size.Y - 1 do
683 for X := 0 to Board.Size.X - 1 do
684 if Board.Tiles[Y, X].Action = taMove then begin
685 MetaCanvas.Brush.Color := GetTileColor(Board.Tiles[Y, X].Value);
686 MetaCanvas.Brush.Style := bsSolid;
687 TileRect := Bounds(
688 Frame.Left + X * TileSize.X + Trunc(Board.Tiles[Y, X].Shift.X / 100 * TileSize.X + TileMargin),
689 Frame.Top + Y * TileSize.Y + Trunc(Board.Tiles[Y, X].Shift.Y / 100 * TileSize.Y + TileMargin),
690 TileSize.X - 2 * TileMargin, TileSize.Y - 2 * TileMargin);
691 RenderTile(MetaCanvas, Board.Tiles[Y, X], TileRect, True);
692 end;
693
694 // Draw appearing tiles
695 for Y := 0 to Board.Size.Y - 1 do
696 for X := 0 to Board.Size.X - 1 do
697 if Board.Tiles[Y, X].Action = taAppear then begin
698 MetaCanvas.Brush.Color := GetTileColor(Board.Tiles[Y, X].Value);
699 MetaCanvas.Brush.Style := bsSolid;
700 TileRect := Bounds(
701 Frame.Left + X * TileSize.X + TileMargin,
702 Frame.Top + Y * TileSize.Y + TileMargin,
703 TileSize.X - 2 * TileMargin,
704 TileSize.Y - 2 * TileMargin);
705 TileCenter := TileRect.CenterPoint;
706 S := Point(
707 Trunc(Board.Tiles[Y, X].Shift.X / 100 * (TileSize.X - TileMargin)),
708 Trunc(Board.Tiles[Y, X].Shift.Y / 100 * (TileSize.Y - TileMargin))
709 );
710 TileRect := Rect(TileCenter.X - S.X div 2, TileCenter.Y - S.Y div 2,
711 TileCenter.X + S.X div 2, TileCenter.Y + S.Y div 2);
712 RenderTile(MetaCanvas, Board.Tiles[Y, X], TileRect, True);
713 end;
714
715 // Draw merging tiles
716 for Y := 0 to Board.Size.Y - 1 do
717 for X := 0 to Board.Size.X - 1 do
718 if Board.Tiles[Y, X].Action = taMerge then begin
719 MetaCanvas.Brush.Color := GetTileColor(Board.Tiles[Y, X].Value);
720 MetaCanvas.Brush.Style := bsSolid;
721 TileRect := Bounds(
722 Frame.Left + X * TileSize.X + TileMargin,
723 Frame.Top + Y * TileSize.Y + TileMargin,
724 TileSize.X - 2 * TileMargin,
725 TileSize.Y - 2 * TileMargin);
726 S := Point(
727 Trunc((50 - Abs(Board.Tiles[Y, X].Shift.X - 50)) / 50 * TileMargin),
728 Trunc((50 - Abs(Board.Tiles[Y, X].Shift.Y - 50)) / 50 * TileMargin)
729 );
730 TileRect := Rect(TileRect.Left - S.X, TileRect.Top - S.Y,
731 TileRect.Right + S.X, TileRect.Bottom + S.Y);
732 RenderTile(MetaCanvas, Board.Tiles[Y, X], TileRect, True);
733 end;
734
735 MetaCanvas.DrawTo(Canvas);
736 MetaCanvas.Free;
737end;
738
739procedure TGame.MoveAll(Direction: TMoveDirection; Animation: Boolean);
740var
741 P: TPoint;
742 PNew: TPoint;
743 I: Integer;
744 Area: TArea;
745 DstTile: TTile;
746 SrcTile: TTile;
747 TileMoved: Boolean;
748begin
749 if Animation then begin
750 MoveAllAnimate(Direction);
751 Exit;
752 end;
753 if Direction = drNone then Exit;
754 if not CanMoveDirection(Direction) then Exit;
755 FMoving := True;
756 FBoardUndo.Assign(Board);
757 FCanUndo := True;
758 Area := GetMoveArea(Direction);
759 Board.ClearMerged;
760 for I := 0 to Max(Board.Size.X, Board.Size.Y) - 1 do begin
761 TileMoved := False;
762 P := Area.P1;
763 while P.Y <> Area.P2.Y + Area.Increment.Y do begin
764 P.X := Area.P1.X;
765 while P.X <> Area.P2.X + Area.Increment.X do begin
766 PNew := P + DirectionDiff[Direction];
767 if IsValidPos(PNew) then begin
768 SrcTile := Board.Tiles[P.Y, P.X];
769 DstTile := Board.Tiles[PNew.Y, PNew.X];
770 if SrcTile.Value <> 0 then begin
771 if DstTile.Value = 0 then begin
772 DstTile.Value := SrcTile.Value;
773 DstTile.Merged := SrcTile.Merged;
774 SrcTile.Value := 0;
775 SrcTile.Merged := False;
776 TileMoved := True;
777 end else
778 if (not SrcTile.Merged) and (not DstTile.Merged) and
779 CanMergeTile(DstTile.Value, SrcTile.Value) then begin
780 DstTile.Value := MergeTile(DstTile.Value, SrcTile.Value);
781 DstTile.Merged := True;
782 SrcTile.Value := 0;
783 SrcTile.Merged := False;
784 Score := Score + GetTileSkinScore(SrcTile.Value);
785 TileMoved := True;
786 end;
787 end;
788 end;
789 Inc(P.X, Area.Increment.X);
790 end;
791 Inc(P.Y, Area.Increment.Y);
792 end;
793 if not TileMoved then Break;
794 end;
795 DoPaint;
796 FMoving := False;
797end;
798
799procedure TGame.RenderTile(Canvas: TCanvas; Tile: TTile; TileRect: TRect; WithText: Boolean);
800var
801 ValueStr: string;
802 TextSize: TSize;
803begin
804 Canvas.Pen.Style := psClear;
805 Canvas.RoundRect(TileRect, ScaleX(TileRect.Width div 20, 96), ScaleY(TileRect.Height div 20, 96));
806 if WithText and (Tile.Value <> 0) then begin
807 ValueStr := GetTileSkinValue(Tile.Value);
808 Canvas.Brush.Style := bsClear;
809 Canvas.Font.Height := Trunc(TileRect.Height * 0.7);
810 TextSize := Canvas.TextExtent(ValueStr);
811 if TextSize.Width > TileRect.Width then
812 Canvas.Font.Height := Trunc(Canvas.Font.Height / TextSize.Width * TileRect.Width);
813 TextSize := Canvas.TextExtent(ValueStr);
814 Canvas.TextOut(TileRect.Left + TileRect.Width div 2 - TextSize.Width div 2,
815 TileRect.Top + TileRect.Height div 2 - TextSize.Height div 2, ValueStr);
816 end;
817end;
818
819function TGame.CanUndo: Boolean;
820begin
821 Result := UndoEnabled and FCanUndo;
822end;
823
824procedure TGame.Undo;
825begin
826 if UndoEnabled and CanUndo then begin
827 Board.Assign(FBoardUndo);
828 FCanUndo := False;
829 FRunning := CanMove;
830 if RecordHistory then History.Moves.Delete(History.Moves.Count - 1);
831 DoChange;
832 DoPaint;
833 end;
834end;
835
836function TGame.CanMergeDirection(Direction: TMoveDirection): Boolean;
837var
838 P: TPoint;
839 PNew: TPoint;
840 I: Integer;
841 Area: TArea;
842begin
843 Result := False;
844 if Direction = drNone then Exit;
845 Area := GetMoveArea(Direction);
846 for I := 0 to Max(Board.Size.X, Board.Size.Y) - 1 do begin
847 P := Area.P1;
848 while P.Y <> Area.P2.Y + Area.Increment.Y do begin
849 P.X := Area.P1.X;
850 while P.X <> Area.P2.X + Area.Increment.X do begin
851 PNew := P + DirectionDiff[Direction];
852 if IsValidPos(PNew) then begin
853 if (Board.Tiles[PNew.Y, PNew.X].Value = 0) then begin
854 Board.Tiles[PNew.Y, PNew.X].Value := Board.Tiles[P.Y, P.X].Value;
855 Board.Tiles[P.Y, P.X].Value := 0;
856 end else
857 if (Board.Tiles[P.Y, P.X].Value <> 0) then begin
858 if CanMergeTile(Board.Tiles[PNew.Y, PNew.X].Value, Board.Tiles[P.Y, P.X].Value) then begin
859 Result := True;
860 Break;
861 end;
862 end;
863 end;
864 Inc(P.X, Area.Increment.Y);
865 end;
866 if Result then Break;
867 Inc(P.Y, Area.Increment.Y);
868 end;
869 end;
870end;
871
872function TGame.CanMoveDirection(Direction: TMoveDirection): Boolean;
873var
874 P: TPoint;
875 PNew: TPoint;
876 Area: TArea;
877begin
878 Result := False;
879 if Direction = drNone then Exit;
880 Area := GetMoveArea(Direction);
881 P := Area.P1;
882 while P.Y <> Area.P2.Y + Area.Increment.Y do begin
883 P.X := Area.P1.X;
884 while P.X <> Area.P2.X + Area.Increment.X do begin
885 PNew := P + DirectionDiff[Direction];
886 if IsValidPos(PNew) then begin
887 if (Board.Tiles[P.Y, P.X].Value <> 0) then begin
888 if (Board.Tiles[PNew.Y, PNew.X].Value = 0) or
889 CanMergeTile(Board.Tiles[PNew.Y, PNew.X].Value, Board.Tiles[P.Y, P.X].Value) then begin
890 Result := True;
891 Break;
892 end;
893 end;
894 end;
895 Inc(P.X, Area.Increment.X);
896 end;
897 if Result then Break;
898 Inc(P.Y, Area.Increment.Y);
899 end;
900end;
901
902procedure TGame.MoveAllAnimate(Direction: TMoveDirection);
903var
904 P: TPoint;
905 PNew: TPoint;
906 X, Y: Integer;
907 I: Integer;
908 StartTime: TDateTime;
909 EndTime: TDateTime;
910 Time: TDateTime;
911 Part: Double;
912 Area: TArea;
913 TileMoved: Boolean;
914begin
915 if Direction = drNone then Exit;
916 if not CanMoveDirection(Direction) then Exit;
917 FMoving := True;
918 FBoardUndo.Assign(Board);
919 FCanUndo := True;
920 Area := GetMoveArea(Direction);
921 Board.ClearMerged;
922 for I := 0 to Max(Board.Size.X, Board.Size.Y) - 1 do begin
923 for Y := 0 to Board.Size.Y - 1 do
924 for X := 0 to Board.Size.X - 1 do begin
925 Board.Tiles[Y, X].NewValue := Board.Tiles[Y, X].Value;
926 Board.Tiles[Y, X].Action := taNone;
927 end;
928
929 TileMoved := False;
930 P := Area.P1;
931 while P.Y <> Area.P2.Y + Area.Increment.Y do begin
932 P.X := Area.P1.X;
933 while P.X <> Area.P2.X + Area.Increment.X do begin
934 PNew := P + DirectionDiff[Direction];
935 if IsValidPos(PNew) then begin
936 if (Board.Tiles[P.Y, P.X].NewValue <> 0) then begin
937 if (Board.Tiles[PNew.Y, PNew.X].NewValue = 0) then begin
938 Board.Tiles[P.Y, P.X].Action := taMove;
939 Board.Tiles[PNew.Y, PNew.X].NewValue := Board.Tiles[P.Y, P.X].NewValue;
940 Board.Tiles[PNew.Y, PNew.X].Merged := Board.Tiles[P.Y, P.X].Merged;
941 Board.Tiles[P.Y, P.X].NewValue := 0;
942 Board.Tiles[P.Y, P.X].Merged := False;
943 TileMoved := True;
944 end else
945 if (not Board.Tiles[P.Y, P.X].Merged) and (not Board.Tiles[PNew.Y, PNew.X].Merged) and
946 CanMergeTile(Board.Tiles[PNew.Y, PNew.X].NewValue, Board.Tiles[P.Y, P.X].NewValue) then begin
947 Board.Tiles[P.Y, P.X].Action := taMove;
948 Board.Tiles[PNew.Y, PNew.X].NewValue := MergeTile(Board.Tiles[PNew.Y, PNew.X].NewValue, Board.Tiles[P.Y, P.X].NewValue);
949 Board.Tiles[PNew.Y, PNew.X].Merged := True;
950 Board.Tiles[P.Y, P.X].NewValue := 0;
951 Board.Tiles[P.Y, P.X].Merged := False;
952 Score := Score + GetTileSkinScore(Board.Tiles[PNew.Y, PNew.X].NewValue);
953 TileMoved := True;
954 end;
955 end;
956 end;
957 Inc(P.X, Area.Increment.X);
958 end;
959 Inc(P.Y, Area.Increment.Y);
960 end;
961 if not TileMoved then Break;
962
963 // Animate tiles move
964 StartTime := Now;
965 EndTime := StartTime + AnimationDuration / 300 * OneSecond / Max(Board.Size.X, Board.Size.Y);
966 if AnimationDuration > 0 then
967 repeat
968 Time := Now;
969 Part := (Time - StartTime) / (EndTime - StartTime);
970 if Part > 1 then Part := 1;
971 for Y := 0 to Board.Size.Y - 1 do
972 for X := 0 to Board.Size.X - 1 do begin
973 if Board.Tiles[Y, X].Action = taMove then
974 Board.Tiles[Y, X].Shift := Point(Trunc(Part * DirectionDiff[Direction].X * 100),
975 Trunc(Part * DirectionDiff[Direction].Y * 100));
976 end;
977 DoPaint;
978 //Application.ProcessMessages;
979 Sleep(AnimationTick);
980 until Time > EndTime;
981
982 // Set final tiles values
983 for Y := 0 to Board.Size.Y - 1 do
984 for X := 0 to Board.Size.X - 1 do begin
985 Board.Tiles[Y, X].Value := Board.Tiles[Y, X].NewValue;
986 end;
987 end;
988
989 // Set final tiles values
990 for Y := 0 to Board.Size.Y - 1 do
991 for X := 0 to Board.Size.X - 1 do begin
992 if Board.Tiles[Y, X].Merged then
993 Board.Tiles[Y, X].Action := taMerge;
994 Board.Tiles[Y, X].Shift := Point(0, 0);
995 if Board.Tiles[Y, X].Action = taMove then begin
996 Board.Tiles[Y, X].Action := taNone;
997 end;
998 Board.Tiles[Y, X].Value := Board.Tiles[Y, X].NewValue;
999 end;
1000 DoPaint;
1001 FMoving := False;
1002end;
1003
1004function TGame.CanMergeTile(Value1, Value2: Integer): Boolean;
1005begin
1006 Result := MergeTile(Value1, Value2) <> -1;
1007end;
1008
1009function TGame.MergeTile(Value1, Value2: Integer): Integer;
1010begin
1011 if Value1 = Value2 then Result := Value1 + 1
1012 else Result := -1;
1013end;
1014
1015procedure TGame.AnimateTiles;
1016var
1017 StartTime: TDateTime;
1018 EndTime: TDateTime;
1019 Time: TDateTime;
1020 Part: Double;
1021 X, Y: Integer;
1022begin
1023 FMoving := True;
1024
1025 // Animate tiles move
1026 StartTime := Now;
1027 EndTime := StartTime + AnimationDuration / 300 * OneSecond / Max(Board.Size.X, Board.Size.Y);
1028 if AnimationDuration > 0 then
1029 repeat
1030 Time := Now;
1031 Part := (Time - StartTime) / (EndTime - StartTime);
1032 if Part > 1 then Part := 1;
1033 for Y := 0 to Board.Size.Y - 1 do
1034 for X := 0 to Board.Size.X - 1 do begin
1035 if Board.Tiles[Y, X].Action = taAppear then
1036 Board.Tiles[Y, X].Shift := Point(Trunc(Part * 100), Trunc(Part * 100));
1037 if Board.Tiles[Y, X].Action = taMerge then
1038 Board.Tiles[Y, X].Shift := Point(Trunc(Part * 100), Trunc(Part * 100));
1039 end;
1040 DoPaint;
1041 //Application.ProcessMessages;
1042 Sleep(AnimationTick);
1043 until Time > EndTime;
1044
1045 for Y := 0 to Board.Size.Y - 1 do
1046 for X := 0 to Board.Size.X - 1 do
1047 if Board.Tiles[Y, X].Action <> taNone then begin
1048 Board.Tiles[Y, X].Action := taNone;
1049 Board.Tiles[Y, X].Shift := Point(0, 0);
1050 end;
1051 DoPaint;
1052 FMoving := False;
1053end;
1054
1055function IntToStrRoman(Num: Integer): string;
1056const
1057 Nvals = 13;
1058 Vals: array [1..Nvals] of Word =
1059 (1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000);
1060 Roms: array [1..Nvals] of string[2] =
1061 ('I', 'IV', 'V', 'IX', 'X', 'XL', 'L', 'XC', 'C', 'CD', 'D', 'CM', 'M');
1062var
1063 B: 1..Nvals;
1064begin
1065 Result := '';
1066 B := Nvals;
1067 while Num > 0 do
1068 begin
1069 while Vals[b] > Num do
1070 Dec(B);
1071 Dec(Num, Vals[B]);
1072 Result := Result + Roms[B];
1073 end;
1074end;
1075
1076function IntToBin(Num: Integer): string;
1077begin
1078 Result := '';
1079 while Num > 0 do begin
1080 Result := IntToStr(Num mod 2) + Result;
1081 Num := Num shr 1;
1082 end;
1083end;
1084
1085function TGame.GetTileSkinValue(Value: Integer): string;
1086begin
1087 case FSkin of
1088 tsLinear: Result := IntToStr(Value);
1089 tsPowerOfTwo: Result := IntToStr(1 shl Value);
1090 tsAlpha: Result := Chr(Ord('A') + Value - 1);
1091 tsRoman: Result := IntToStrRoman(Value);
1092 tsBinary: Result := IntToBin(Value);
1093 else Result := IntToStr(Value);
1094 end;
1095end;
1096
1097function TGame.GetTileSkinScore(Value: Integer): Integer;
1098begin
1099 case FSkin of
1100 tsLinear: Result := 1 shl Value;
1101 tsPowerOfTwo: Result := 1 shl Value;
1102 tsAlpha: Result := 1 shl Value;
1103 tsRoman: Result := 1 shl Value;
1104 tsBinary: Result := 1 shl Value;
1105 else Result := 1 shl Value;
1106 end;
1107end;
1108
1109procedure TGame.MoveAllAndUpdate(Direction: TMoveDirection; Animation: Boolean);
1110var
1111 HighestValue: Integer;
1112 HistoryMove: THistoryMove;
1113 NewTile: TTile;
1114 X, Y: Integer;
1115begin
1116 if CanMoveDirection(Direction) then begin
1117 HighestValue := Board.GetHighestTileValue;
1118 MoveAll(Direction, Animation);
1119
1120 NewTile := FillRandomTile;
1121 if Animation then AnimateTiles else begin
1122 for Y := 0 to Board.Size.Y - 1 do
1123 for X := 0 to Board.Size.X - 1 do
1124 if Board.Tiles[Y, X].Action <> taNone then begin
1125 Board.Tiles[Y, X].Action := taNone;
1126 Board.Tiles[Y, X].Shift := Point(0, 0);
1127 end;
1128 DoPaint;
1129 end;
1130
1131 if RecordHistory then begin
1132 HistoryMove := THistoryMove.Create;
1133 HistoryMove.Direction := Direction;
1134 HistoryMove.NewItemPos := NewTile.Index;
1135 HistoryMove.NewItemValue := NewTile.Value;
1136 History.Moves.Add(HistoryMove);
1137 end;
1138
1139 if not CanMove and (Board.GetEmptyTilesCount = 0) then
1140 GameOver;
1141 if (HighestValue < WinTileValue) and
1142 (Board.GetHighestTileValue >= WinTileValue) then Win;
1143 DoChange;
1144 end;
1145end;
1146
1147procedure TGame.MoveTile(SourceTile, TargetTile: TTile);
1148begin
1149 TargetTile.Value := SourceTile.Value;
1150 SourceTile.Value := 0;
1151 TargetTile.Merged := SourceTile.Merged;
1152 SourceTile.Merged := False;
1153end;
1154
1155function TGame.IsValidPos(Pos: TPoint): Boolean;
1156begin
1157 Result := (Pos.X >= 0) and (Pos.X < Board.Size.X) and
1158 (Pos.Y >= 0) and (Pos.Y < Board.Size.Y);
1159end;
1160
1161procedure TGame.SaveToRegistry(RegContext: TRegistryContext);
1162var
1163 Reg: TRegistryEx;
1164begin
1165 Reg := TRegistryEx.Create;
1166 with Reg do
1167 try
1168 CurrentContext := RegContext;
1169
1170 WriteInteger('TopScore', TopScore);
1171 WriteInteger('AnimationDuration', AnimationDuration);
1172 WriteInteger('Score', Score);
1173 WriteBool('GameRunning', FRunning);
1174 WriteBool('CanUndo', FCanUndo);
1175 WriteBool('UndoEnabled', UndoEnabled);
1176 WriteBool('RecordHistory', RecordHistory);
1177 WriteInteger('Skin', Integer(Skin));
1178 FBoardUndo.SaveToRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\BoardUndo'));
1179 Board.SaveToRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\Board'));
1180 History.SaveToRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\History'));
1181 finally
1182 Free;
1183 end;
1184end;
1185
1186procedure TGame.LoadFromRegistry(RegContext: TRegistryContext);
1187var
1188 Reg: TRegistryEx;
1189begin
1190 Reg := TRegistryEx.Create;
1191 with Reg do
1192 try
1193 CurrentContext := RegContext;
1194 AnimationDuration := ReadIntegerWithDefault('AnimationDuration', 30);
1195 TopScore := ReadIntegerWithDefault('TopScore', 0);
1196 Score := ReadIntegerWithDefault('Score', 0);
1197 FRunning := ReadBoolWithDefault('GameRunning', False);
1198 FCanUndo := ReadBoolWithDefault('CanUndo', False);
1199 UndoEnabled := ReadBoolWithDefault('UndoEnabled', True);
1200 RecordHistory := ReadBoolWithDefault('RecordHistory', False);
1201 Skin := TTileSkin(ReadIntegerWithDefault('Skin', Integer(tsPowerOfTwo)));
1202 FBoardUndo.LoadFromRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\BoardUndo'));
1203 Board.LoadFromRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\Board'));
1204 History.LoadFromRegistry(Reg, TRegistryContext.Create(RegContext.RootKey, RegContext.Key + '\History'));
1205 finally
1206 Free;
1207 end;
1208 DoChange;
1209 DoPaint;
1210end;
1211
1212constructor TGame.Create;
1213begin
1214 AnimationDuration := 30;
1215 AnimationTick := 10; // ms
1216 WinTileValue := 11; // 2^11 = 2048
1217 Board := TBoard.Create;
1218 FBoardUndo := TBoard.Create;
1219 History := THistory.Create;
1220 History.Game := Self;
1221 Value2Chance := 0.1;
1222end;
1223
1224destructor TGame.Destroy;
1225begin
1226 FreeAndNil(History);
1227 FreeAndNil(FBoardUndo);
1228 FreeAndNil(Board);
1229 inherited;
1230end;
1231
1232function TGame.GetTileColor(Value: Integer): TColor;
1233begin
1234 if Core.ThemeManager1.Theme.Name = 'Dark' then begin
1235 case Value of
1236 0: Result := $222629;
1237 1: Result := $dae4ee;
1238 2: Result := $c8e0ed;
1239 3: Result := $79b1f2;
1240 4: Result := $6395f5;
1241 5: Result := $5f7cf6;
1242 6: Result := $3b5ef6;
1243 7: Result := $72cfed;
1244 8: Result := $61cced;
1245 9: Result := $50c8ed;
1246 10: Result := $3fc5ed;
1247 11: Result := $2ec2ed;
1248 else Result := $323a3c;
1249 end;
1250 end else begin
1251 case Value of
1252 0: Result := $f2f6f9;
1253 1: Result := $dae4ee;
1254 2: Result := $c8e0ed;
1255 3: Result := $79b1f2;
1256 4: Result := $6395f5;
1257 5: Result := $5f7cf6;
1258 6: Result := $3b5ef6;
1259 7: Result := $72cfed;
1260 8: Result := $61cced;
1261 9: Result := $50c8ed;
1262 10: Result := $3fc5ed;
1263 11: Result := $2ec2ed;
1264 else Result := $323a3c;
1265 end;
1266 end;
1267end;
1268
1269procedure TGame.SetRecordHistory(AValue: Boolean);
1270begin
1271 if FRecordHistory = AValue then Exit;
1272 FRecordHistory := AValue;
1273 if not FRecordHistory then History.Clear;
1274end;
1275
1276procedure TGame.SetScore(AValue: Integer);
1277begin
1278 if FScore = AValue then Exit;
1279 FScore := AValue;
1280 if FScore > TopScore then TopScore := FScore;
1281end;
1282
1283initialization
1284 Translate;
1285
1286end.
1287
Note: See TracBrowser for help on using the repository browser.