6cf8e759b6c0ea3159de2cec7b8cb9f7c01b1918
[sdk] / samples / games / blokus / blokus.ec
1 import "ecere"
2 import "console"
3 public enum BlokusColor : byte { none, blue, yellow, red, green };
4
5 public enum PlayerColor : byte
6 {
7    blue, yellow, red, green;
8    property BlokusColor
9    {
10       // TOFIX: get { return (BlokusColor)((int)this+1); }
11       get { return (BlokusColor)*(int *)&this+1; }
12    }
13 };
14
15 static Color colors[2][BlokusColor] =
16 {
17    // TOFIX: (omitting Color { })
18    { 0, Color { 64, 64, 96 }, Color { 141, 114, 48 }, Color { 96, 32, 32 }, Color { 64, 96, 64 } },
19    { 0, blue, goldenrod, red, green }
20 };
21 Point corners[PlayerColor] =
22 {
23    { 0, 0 },
24    { boardSize-1, 0 },
25    { boardSize-1, boardSize-1 },
26    { 0, boardSize-1 }
27 };
28
29 define numPieces = 21;
30 #ifdef _DEBUG
31 define boardSize = 20; //10;
32 #else
33 define boardSize = 20;
34 #endif
35
36 #if defined(HIGH_DPI)
37 define squareWidth = 60;
38 define boardStartX = 20;
39 define boardStartY = 30;
40 define piecesY = 20;
41 define blockOffset0 = 4;
42 define blockOffset1 = 10;
43 define blockOffset2 = 30;
44 define blockOffset3 = 36;
45 #else
46 define squareWidth = 28;
47 define blockOffset0 = 2;
48 define blockOffset1 = 5;
49 define blockOffset2 = 15;
50 define blockOffset3 = 18;
51 define boardStartX = 20;
52 define boardStartY = 30;
53 define piecesY = 20;
54 #endif
55
56 struct Piece
57 {
58    int w, h;
59    byte blocks[25];
60 };
61
62 Piece pieces[numPieces] =
63 {
64    {
65       1, 1,
66       { 1 }
67    },
68    {
69       1, 2,
70       {
71          1,
72          1
73       }
74    },
75    {
76       1,3,
77       {
78          1,
79          1,
80          1
81       }
82    },
83    {
84       2, 2,
85       {
86          1,0,
87          1,1
88       }
89    },
90    {
91       1, 4,
92       {
93          1,
94          1,
95          1,
96          1
97       }
98    }
99    {
100       2,3,
101       {
102          0,1,
103          0,1,
104          1,1
105       }
106    },
107    {
108       2, 3,
109       {
110          1,0,
111          1,1,
112          1,0
113       }
114    },
115    {
116       2,2,
117       {
118          1,1,
119          1,1
120       }
121    },
122    {
123       3,2,
124       {
125          1,1,0,
126          0,1,1
127       }
128    },
129    {
130        1, 5,
131       {
132          1,
133          1,
134          1,
135          1,
136          1
137       }
138    },
139    {
140       2, 4,
141       {
142          0,1,
143          0,1,
144          0,1,
145          1,1
146       }
147    },
148    {
149       2, 4,
150       {
151          0,1,
152          0,1,
153          1,1,
154          1,0
155       }
156    },
157    {
158       2,3,
159       {
160          0,1,
161          1,1,
162          1,1
163       }
164    },
165    {
166       2,3,
167       {
168          1,1,
169          0,1,
170          1,1
171       }
172    },
173    {
174       2,4,
175       {
176          1,0,
177          1,1,
178          1,0,
179          1,0
180       }
181    },
182    {
183       3,3,
184       {
185          0,1,0,
186          0,1,0,
187          1,1,1
188       }
189    },
190    {
191       3,3,
192       {
193          1,0,0,
194          1,0,0,
195          1,1,1
196       }
197    },
198    {
199       3,3,
200       {
201          1,1,0,
202          0,1,1,
203          0,0,1
204       }
205    },
206    {
207       3,3,
208       {
209          1,0,0,
210          1,1,1,
211          0,0,1
212       }
213    },
214    {
215       3,3,
216       {
217          1,0,0,
218          1,1,1,
219          0,1,0
220       }
221    },
222    {
223       3,3,
224       {
225          0,1,0,
226          1,1,1,
227          0,1,0
228       }
229    }
230 };
231
232 struct BlokusGameState
233 {
234    byte playerPieces[PlayerColor][numPieces];
235    bool firstPiece[PlayerColor];
236    BlokusColor board[boardSize * boardSize];
237    PlayerColor colorTurn;
238    PlayerColor rotatingColor;
239    int numPlayers;
240    bool validPieces[numPieces];
241    bool over;
242    bool validMove;
243    int bonus[4];
244    int scores[4];
245
246    void NewGame()
247    {
248       PlayerColor p;
249       int i;
250
251       // TOFIX: for(p = 0; p < PlayerColor::enumSize; p++)
252       for(p = blue; p <= green; p++)
253       {
254          for(i = 0; i < numPieces; i++)
255             playerPieces[p][i] = 1;
256          firstPiece[p] = true;
257          bonus[p] = 0;
258          scores[0] = 0;
259       }
260       for(i = 0; i < boardSize * boardSize; i++)
261          board[i] = none;
262
263       over = false;
264       colorTurn = blue;
265       rotatingColor = blue;
266
267       validMove = CheckValidMoves(colorTurn, validPieces);
268    }
269
270    bool ValidMove(PlayerColor playerColor, int selectedPiece, int direction, bool flip, int boardX, int boardY)
271    {
272       Piece * piece = &pieces[selectedPiece];
273       int x, y;
274       bool valid = true;
275       bool touchCorner = false;
276       int w = (direction & 1) ? piece->h : piece->w;
277       int h = (direction & 1) ? piece->w : piece->h;
278
279       if(!playerPieces[playerColor][selectedPiece]) return false;
280
281       if(boardX < 0 || boardY < 0 || boardX + w > boardSize || boardY + h > boardSize) return false;
282
283       for(y = 0; y < 5; y++)
284          for(x = 0; x < 5; x++)
285             if(PieceBlock(selectedPiece, x, y, direction, flip))
286             {
287                int bx = x + boardX, by = y + boardY;
288                if(board[by * boardSize + bx] ||
289                   (by > 0    && board[(by-1) * boardSize + bx] == playerColor) ||
290                   (by < boardSize-1 && board[(by+1) * boardSize + bx] == playerColor) ||
291                   (bx > 0    && board[by * boardSize + bx - 1] == playerColor) ||
292                   (bx < boardSize-1 && board[by * boardSize + bx + 1] == playerColor))
293                {
294                   valid = false;
295                   break;
296                }
297                if((by > 0     && bx > 0    && board[(by-1) * boardSize + (bx-1)] == playerColor) ||
298                   (by > 0     && bx < boardSize-1 && board[(by-1) * boardSize + (bx+1)] == playerColor) ||
299                   (by < boardSize-1  && bx > 0    && board[(by+1) * boardSize + (bx-1)] == playerColor) ||
300                   (by < boardSize-1  && bx < boardSize-1 && board[(by+1) * boardSize + (bx+1)] == playerColor))
301                   touchCorner = true;
302             }
303       if(valid && firstPiece[playerColor])
304       {
305          for(y = 0; y < 5; y++)
306             for(x = 0; x < 5; x++)
307                if(PieceBlock(selectedPiece, x, y, direction, flip))
308                {
309                   int bx = x + boardX, by = y + boardY;
310                   if(bx == corners[playerColor].x && by == corners[playerColor].y)
311                   {
312                      touchCorner = true;
313                      break;
314                   }
315                }
316       }
317
318       return valid && touchCorner;
319    }
320
321    bool CheckValidMoves(PlayerColor playerColor, bool validPieces[21])
322    {
323       bool result = false;
324       int p;
325       for(p = 0; p < numPieces; p++)
326       {
327          bool validMove = false;
328          if(playerPieces[playerColor][p])
329          {
330             int x, y;
331             for(y = 0; y < boardSize && !validMove; y++)
332             {
333                for(x = 0; x < boardSize && !validMove; x++)
334                {
335                   int flip;
336                   int direction;
337                   for(direction = 0; direction < 4 && !validMove; direction++)
338                   {
339                      for(flip = 0; flip <=1 && !validMove; flip++)
340                      {
341                         if(ValidMove(playerColor, p, direction, flip, x, y))
342                            result = validMove = true;
343                      }
344                   }
345                }
346             }
347          }
348          if(validPieces) validPieces[p] = validMove;
349       }
350       return result;
351    }
352
353    void PlayMove(int pieceType, int direction, bool flip, int boardX, int boardY)
354    {
355       int p;
356       int y, x;
357       for(y = 0; y < 5; y++)
358          for(x = 0; x < 5; x++)
359             if(PieceBlock(pieceType, x, y, direction, flip))
360                board[(y + boardY) * boardSize + x + boardX] = colorTurn;
361       playerPieces[colorTurn][pieceType] = 0;
362       scores[colorTurn] = 0;
363       for(p = 0; p < numPieces; p++)
364       {
365          if(playerPieces[colorTurn][p])
366          {
367             Piece * piece = &pieces[p];
368             int y, x;
369             for(y = 0; y < piece->h; y++)
370                for(x = 0; x < piece->w; x++)
371                   if(piece->blocks[y * piece->w + x])
372                      scores[colorTurn]--;
373          }
374       }
375       if(scores[colorTurn] == 0)
376          bonus[colorTurn] = (pieceType == 0) ? 20 : 15;
377
378       firstPiece[colorTurn] = false;
379       if(numPlayers == 3 && colorTurn == green)
380       {
381          if(++rotatingColor == green) rotatingColor = 0;
382       }
383       if(++colorTurn > green) colorTurn = 0;
384
385       validMove = CheckValidMoves(colorTurn, validPieces);
386       if(!validMove)
387          CheckGameOver();
388    }
389
390    void Pass()
391    {
392       if(numPlayers == 3 && colorTurn == green)
393       {
394          if(++rotatingColor == green) rotatingColor = 0;
395       }
396       if(++colorTurn > green) colorTurn = 0;
397
398       validMove = CheckValidMoves(colorTurn, validPieces);
399       if(!validMove)
400          CheckGameOver();
401    }
402
403    void CheckGameOver()
404    {
405       PlayerColor turn = colorTurn;
406       int c;
407       for(c = 0; c < 3; c++)
408       {
409          if(++turn > green) turn = 0;
410          if(CheckValidMoves(turn, null))
411             break;
412       }
413       if(c == 3)
414          over = true;
415    }
416 };
417
418 int PieceBlock(int p, int x, int y, int direction, bool flip)
419 {
420    Piece * piece = &pieces[p];
421    int w = piece->w, h = piece->h;
422    int rx, ry;
423    switch(direction)
424    {
425       case 0: rx = x; ry = y; break;
426       case 1: ry = h-1 - x; rx = y; break;
427       case 2: ry = h-1 - y; rx = w -1- x; break;
428       case 3: ry = x; rx = w-1-y; break;
429    }
430    if(rx < w && ry < h && rx  >= 0 && ry >= 0)
431       return piece->blocks[ry * w + (flip ? (w-1-rx) : rx)];
432    return 0;
433 }
434
435 class Blokus : Window
436 {
437    caption = "Ecere Blokus";
438    background = black;
439    minClientSize = { 1068 /*800*/, 700 };
440 #ifdef __ANDROID__
441    anchor = { 0, 0, 0, 0 };
442 #else
443    borderStyle = sizable;
444    hasMaximize = true;
445    hasMinimize = true;
446    hasClose = true;
447    clientSize = { 1276, 708 };
448 #endif
449    font = { "Arial", 12, bold = true };
450    FontResource yourTurnFont { "Arial", 12, bold = true, italic = true, window = this };
451    icon = { ":ollie.png" };
452
453    ServerConnection server;
454
455    bool gameStarted;
456    PlayerColor firstColor;
457    char *playerNames[MaxPlayers];
458
459    // Current game state
460    BlokusGameState gameState;
461
462    // User Interaction
463    PlayerColor colorPlayed; // Color currently (or next) being played
464    int selectedPiece;
465    bool dragging, onBoard;
466    Point drag, offset;
467    Point squareDragged;
468    Point boardPos;
469    int direction;
470    bool flip;
471    bool passed[4];
472
473 #ifdef _DEBUG
474    Timer timer
475    {
476       this, delay = 0.1;
477       // started = true;
478       bool DelayExpired()
479       {
480          if(server)
481             server.SendMessage("Hello :)");
482          return true;
483       }
484    };
485
486    Timer timerPlay
487    {
488       this, delay = 0.05;
489       // started = true;
490       bool DelayExpired()
491       {
492          if(server)
493          {
494             if(gotMove && gameStarted && colorPlayed == gameState.colorTurn)
495             {
496                if(btnPass.visible)
497                   btnPass.NotifyClicked(this, btnPass, 0, 0, 0);
498                else if(gameState.validMove)
499                {
500                   while(true)
501                   {
502                      int p = GetRandom(0, numPieces);
503                      if(gameState.validPieces[p])
504                      {
505                         bool validMove = false;
506                         int x, y;
507                         for(y = 0; y < boardSize && !validMove; y++)
508                         {
509                            for(x = 0; x < boardSize && !validMove; x++)
510                            {
511                               int flip;
512                               int direction;
513                               for(direction = 0; direction < 4 && !validMove; direction++)
514                               {
515                                  for(flip = 0; flip <=1 && !validMove; flip++)
516                                  {
517                                     if(gameState.ValidMove(colorPlayed, p, direction, flip, x, y))
518                                     {
519                                        bool result;
520                                        gotMove = false;
521                                        result = server.PlayPiece(p, direction, flip, x, y);
522                                        validMove = true;
523                                     }
524                                  }
525                               }
526                            }
527                         }
528                         break;
529                      }
530                   }
531                }
532             }
533          }
534          return true;
535       }
536    };
537    bool gotMove;
538    gotMove = true;
539 #endif
540
541 #ifdef __WIN32__
542    // To work around Flash() lockups on Windows...
543    bool flash;
544    Timer flashTimer
545    {
546       this, delay = 0.2, true;
547
548       bool DelayExpired()
549       {
550          if(flash)
551             Window::Flash();
552          flash = false;
553          return true;
554       }
555    };
556
557    void Flash()
558    {
559       flash = true;
560    }
561 #endif
562
563    void NextColorPlayed()
564    {
565       if(gameState.numPlayers == 1)
566       {
567          if(++colorPlayed > green) colorPlayed = 0;
568       }
569       else if(gameState.numPlayers == 2)
570       {
571          if((colorPlayed+=2) > green) colorPlayed = firstColor;
572       }
573       else if(gameState.numPlayers == 3)
574       {
575          colorPlayed = (colorPlayed == firstColor && gameState.rotatingColor == firstColor) ? green : firstColor;
576       }
577    }
578
579    void UpdatePlayerNames()
580    {
581       int c;
582       if(gameState.numPlayers == 1)
583       {
584          for(c = 0; c < 4; c++)
585             playerNames[c] = panel.playerNames[0];
586       }
587       else if(gameState.numPlayers == 2)
588       {
589          for(c = 0; c < 2; c++)
590          {
591             playerNames[c] = panel.playerNames[c];
592             playerNames[c+2] = panel.playerNames[c];
593          }
594       }
595       else if(gameState.numPlayers == 3)
596       {
597          for(c = 0; c < 3; c++)
598             playerNames[c] = panel.playerNames[c];
599          playerNames[3] = panel.playerNames[gameState.rotatingColor];
600       }
601       else if(gameState.numPlayers == 4)
602       {
603          for(c = 0; c < 4; c++)
604             playerNames[c] = panel.playerNames[c];
605       }
606    }
607
608    void DrawSquare(Surface surface, int x, int y, BlokusColor color, int shade)
609    {
610       surface.background = colors[shade][color];
611       surface.Area(x+1, y+1, x + squareWidth-1, y + squareWidth-1);
612       surface.foreground = lightGray;
613       surface.VLine(y+blockOffset1, y + blockOffset2, x + blockOffset1);
614       surface.HLine(x+blockOffset1, x + blockOffset3, y + blockOffset1);
615       surface.foreground = white;
616       surface.Rectangle(x + blockOffset0,y+blockOffset0, x + squareWidth-blockOffset0, y + squareWidth - blockOffset0);
617    }
618
619    bool OnClose(bool parentClosing)
620    {
621       if((gameStarted && !gameState.over) || hosting)
622       {
623          if(MessageBox { type = okCancel,
624             caption = "Ecere Blokus", contents = "Quit Ecere Blokus?" }.Modal() == cancel)
625             return false;
626       }
627       return true;
628    }
629
630    void OnDestroy()
631    {
632       if(server)
633          server.Disconnect(0);
634
635       panel.Destroy(0);
636       scoresPanel.Destroy(0);
637    }
638
639    ~Blokus()
640    {
641       delete server;
642    }
643
644    bool OnMouseMove(int mx, int my, Modifiers mods)
645    {
646       if(dragging)
647       {
648          Piece * piece = &pieces[selectedPiece];
649          int w = (direction & 1) ? piece->h : piece->w;
650          int h = (direction & 1) ? piece->w : piece->h;
651          drag = { offset.x + mx, offset.y + my };
652
653          if(mx - squareDragged.x * squareWidth >= boardStartX - 10 && mx - squareDragged.x * squareWidth < boardStartX + ((boardSize-w)+1) * squareWidth + 10 &&
654             my - squareDragged.y * squareWidth >= boardStartY - 10 && my - squareDragged.y * squareWidth < boardStartY + ((boardSize-h)+1) * squareWidth + 10)
655          {
656             int x, y;
657             x = Max(0,mx - squareDragged.x * squareWidth - boardStartX) / squareWidth;
658             y = Max(0,my - squareDragged.y * squareWidth - boardStartY) / squareWidth;
659             x = Min(x, boardSize-w);
660             y = Min(y, boardSize-h);
661             drag.x = boardStartX + x * squareWidth;
662             drag.y = boardStartY + y * squareWidth;
663             boardPos = { x, y };
664             onBoard = true;
665          }
666          else
667             onBoard = false;
668          Update(null);
669       }
670       return true;
671    }
672
673    bool OnLeftButtonUp(int mx, int my, Modifiers mods)
674    {
675       if(dragging)
676       {
677          if(onBoard)
678          {
679             if(gameStarted && colorPlayed == gameState.colorTurn)
680             {
681                Piece * piece = &pieces[selectedPiece];
682                if(gameState.ValidMove(gameState.colorTurn, selectedPiece, direction, flip, boardPos.x, boardPos.y))
683                   server.PlayPiece(selectedPiece, direction, flip, boardPos.x, boardPos.y);
684             }
685          }
686          dragging = false;
687          ReleaseCapture();
688          Update(null);
689       }
690       return true;
691    }
692
693    bool OnRightButtonDown(int mx, int my, Modifiers mods)
694    {
695       Piece * piece = &pieces[selectedPiece];
696       int x = squareDragged.x, y = squareDragged.y;
697       int w,h;
698
699       w = (direction & 1) ? piece->h : piece->w;
700       h = (direction & 1) ? piece->w : piece->h;
701
702       offset.x += squareDragged.x * squareWidth;
703       offset.y += squareDragged.y * squareWidth;
704       squareDragged = (direction & 1) ? { x, h-1-y } : { w-1-x, y };
705       flip ^= true;
706       offset.x -= squareDragged.x * squareWidth;
707       offset.y -= squareDragged.y * squareWidth;
708
709       OnMouseMove(mx,my,mods);
710
711       Update(null);
712       return true;
713    }
714
715    bool OnSysKeyDown(Key key, unichar ch)
716    {
717       // Temporarily disable chat when dragging to get Android arrow keys
718       if(dragging)
719          chat.disabled = true;
720       return true;
721    }
722
723    bool OnKeyDown(Key key, unichar ch)
724    {
725       chat.disabled = false;
726       return true;
727    }
728
729    bool OnKeyHit(Key key, unichar ch)
730    {
731       if(key == space)
732       {
733          OnRightButtonDown(drag.x - offset.x, drag.y - offset.y, 0);
734       }
735       if(key == wheelDown || key == down || key == left || key == wheelUp || key == up || key == right)
736       {
737          Piece * piece = &pieces[selectedPiece];
738          int mx = drag.x - offset.x, my = drag.y - offset.y;
739          int rx, ry;
740          int x = squareDragged.x, y = squareDragged.y;
741          int w,h;
742          bool isDown = key == wheelDown || key == right || key == down;
743
744          if(isDown)
745          {
746             if(++direction == 4) direction = 0;
747          }
748          else
749          {
750             if(--direction == -1) direction = 3;
751          }
752          w = (direction & 1) ? piece->h : piece->w;
753          h = (direction & 1) ? piece->w : piece->h;
754
755          offset.x += squareDragged.x * squareWidth;
756          offset.y += squareDragged.y * squareWidth;
757          if(isDown)
758             squareDragged = { w-1-y, x };
759          else
760             squareDragged = { y, h-1-x };
761
762          offset.x -= squareDragged.x * squareWidth;
763          offset.y -= squareDragged.y * squareWidth;
764
765          OnMouseMove(mx,my,0);
766
767          Update(null);
768       }
769       return true;
770    }
771
772    bool OnLeftButtonDown(int mx, int my, Modifiers mods)
773    {
774       if( mx > squareWidth * boardSize + 40)
775       {
776          int bx, by;
777          bool selected = false;
778          int pieceNum = 0;
779          Point offset;
780          int rh = 0;
781          int c;
782          // Draw Player Pieces
783          bx = squareWidth * boardSize + 40;
784          by = piecesY;
785          for(c = 0; c < numPieces; c++)
786          {
787             if(gameState.playerPieces[colorPlayed][c])
788             {
789                int w = pieces[c].w, h = pieces[c].h;
790                int y;
791                for(y = 0; y < 5; y++)
792                {
793                   int x;
794                   for(x = 0; x < 5; x++)
795                   {
796                      if(PieceBlock(c, x, y, 0, false))
797                      {
798                         int dx = bx + x * squareWidth;
799                         int dy = by + y * squareWidth;
800                         if(mx >= dx && mx < dx + squareWidth &&
801                            my >= dy && my < dy + squareWidth)
802                         {
803                            selected = true;
804                            pieceNum = c;
805                            offset = { bx - mx, by - my };
806                            squareDragged = { x, y };
807                            break;
808                         }
809                      }
810                      if(selected) break;
811                   }
812                   if(selected) break;
813                }
814                if(selected) break;
815                bx += w * squareWidth + squareWidth;
816                if(h > rh) rh = h;
817                if(bx + 5 * squareWidth > clientSize.w)
818                {
819                   bx = squareWidth * boardSize + 40;
820                   by += (rh+1) * squareWidth;
821                   rh = 0;
822                }
823             }
824          }
825          if(selected)
826          {
827             Update(null);
828             direction = 0;
829             flip = 0;
830             selectedPiece = pieceNum;
831             this.offset = offset;
832             dragging = true;
833             OnMouseMove(mx, my, mods);
834             Capture();
835          }
836       }
837       return true;
838    }
839
840    float lightValue, lightDir;
841    lightValue = 1;
842    lightDir = -.1f;
843    Timer turnLightTimer
844    {
845       this, 0.1, false;
846
847       bool DelayExpired()
848       {
849          lightValue += lightDir;
850          if(lightValue < 0) { lightValue = 0; lightDir = .1f; }
851          if(lightValue > 1) { lightValue = 1; lightDir =-.1f; }
852          Update(null);
853          return true;
854       }
855    };
856
857    void OnRedraw(Surface surface)
858    {
859       int c;
860       int bx = boardStartX;
861       int by = boardStartY;
862       int x, y;
863       char * s;
864       int len, tw;
865       Color turnLight = white;
866       if(gameStarted && !gameState.over)
867       {
868          ColorRGB empty = colors[1][gameState.colorTurn] /*gray*/, full = white;
869          turnLight = ColorRGB
870          {
871             empty.r + lightValue * (full.r - empty.r),
872             empty.g + lightValue * (full.g - empty.g),
873             empty.b + lightValue * (full.b - empty.b)
874          };
875       }
876
877       surface.foreground = aqua;
878       for(c = 0; c <= boardSize; c++)
879       {
880          surface.HLine(bx,bx+squareWidth*boardSize, by + c * squareWidth);
881          surface.VLine(by,by+squareWidth*boardSize, bx + c * squareWidth);
882       }
883
884       surface.background = colors[blokus.gameStarted][blue];
885       x = bx;
886       y = by;
887       surface.Area(x - 10, y - 10, x + 10, y-1);
888       surface.Area(x - 10, y - 10, x - 1, y+10);
889
890       s = playerNames[PlayerColor::blue];
891       if(s)
892       {
893          len = strlen(s);
894          surface.foreground = (gameState.colorTurn == blue) ? turnLight : white;
895          surface.WriteText(x + 15, y - 20, s, len);
896       }
897
898       surface.background = colors[blokus.gameStarted][yellow];
899       x = bx + boardSize*squareWidth;
900       y = by;
901       surface.Area(x - 10, y - 10, x + 10, y-1);
902       surface.Area(x + 1, y - 10, x + 10,  y+10);
903
904       s = playerNames[PlayerColor::yellow];
905       if(s)
906       {
907          len = strlen(s);
908          surface.TextExtent(s, len, &tw, null);
909          surface.foreground = (gameState.colorTurn == yellow) ? turnLight : white;
910          surface.WriteText(x - 15 - tw, y - 20, s, len);
911       }
912
913       surface.background = colors[blokus.gameStarted][red];
914       x = bx + boardSize*squareWidth;
915       y = by + boardSize*squareWidth;
916       surface.Area(x - 10, y + 1, x + 10, y+10);
917       surface.Area(x + 1, y - 10, x + 10,  y+10);
918       s = playerNames[PlayerColor::red];
919       if(s)
920       {
921          len = strlen(s);
922          surface.TextExtent(s, len, &tw, null);
923          surface.foreground = (gameState.colorTurn == red) ? turnLight : white;
924          surface.WriteText(x - 15 - tw, y, s, len);
925       }
926
927       surface.background = colors[blokus.gameStarted][green];
928       x = bx;
929       y = by + boardSize*squareWidth;
930       surface.Area(x - 10, y + 1, x + 10, y+10);
931       surface.Area(x - 10, y - 10, x - 1,  y+10);
932       s = playerNames[PlayerColor::green];
933       if(s)
934       {
935          len = strlen(s);
936          surface.foreground = (gameState.colorTurn == green) ? turnLight : white;
937          surface.WriteText(x + 15, y, s, len);
938       }
939
940       if(gameState.over)
941       {
942          surface.font = yourTurnFont.font;
943          surface.foreground = crimson;
944          surface.CenterTextf(x + boardSize*squareWidth/2, y + 3, "Game Over");
945       }
946       else if(gameState.numPlayers > 1 && gameStarted && colorPlayed == gameState.colorTurn)
947       {
948          surface.font = yourTurnFont.font;
949          surface.foreground = tomato;
950          surface.CenterTextf(x + boardSize*squareWidth/2, y + 3, "Your turn");
951       }
952
953       for(y = 0; y < boardSize; y++)
954       {
955          for(x = 0; x < boardSize; x++)
956          {
957             BlokusColor color = gameState.board[y * boardSize + x];
958             if(color)
959             {
960                DrawSquare(surface, bx + x * squareWidth, by + y * squareWidth, color, blokus.gameStarted);
961             }
962          }
963       }
964
965       {
966          int rh = 0;
967          // Draw Player Pieces
968          bx = squareWidth * boardSize + 40;
969          by = piecesY;
970          for(c = 0; c < numPieces; c++)
971          {
972             if(gameState.playerPieces[colorPlayed][c])
973             {
974                int w = pieces[c].w, h = pieces[c].h;
975                for(y = 0; y < 5; y++)
976                {
977                   for(x = 0; x < 5; x++)
978                   {
979                      if(PieceBlock(c, x, y, 0, false))
980                      {
981                         if(!dragging || selectedPiece != c)
982                            DrawSquare(surface,
983                               bx + x * squareWidth,
984                               by + y * squareWidth,
985                               colorPlayed,
986                               gameStarted && (gameState.colorTurn == colorPlayed && gameState.validPieces[c]));
987                      }
988                   }
989                }
990                if(h > rh) rh = h;
991                bx += w * squareWidth + squareWidth;
992                if(bx + 5 * squareWidth > clientSize.w)
993                {
994                   bx = squareWidth * boardSize + 40;
995                   by += rh * squareWidth + squareWidth;
996                   rh = 0;
997                }
998             }
999          }
1000          // Draw Dragged piece
1001          if(dragging)
1002          {
1003             for(y = 0; y < 5; y++)
1004                for(x = 0; x < 5; x++)
1005                {
1006                   if(PieceBlock(selectedPiece, x, y, direction, flip))
1007                   {
1008                      DrawSquare(surface,
1009                         drag.x + x * squareWidth,
1010                         drag.y + y * squareWidth,
1011                         colorPlayed,
1012                         gameStarted && gameState.colorTurn == colorPlayed && gameState.validPieces[selectedPiece]);
1013
1014                      if(x == 0 || !PieceBlock(selectedPiece, x-1, y, direction, flip))
1015                      {
1016                         surface.foreground = white;
1017                         surface.VLine(drag.y + y * squareWidth-1, drag.y + (y+1) * squareWidth+1, drag.x + x * squareWidth - 1);
1018                         surface.foreground = lime;
1019                         surface.VLine(drag.y + y * squareWidth-2, drag.y + (y+1) * squareWidth+2, drag.x + x * squareWidth - 2);
1020                      }
1021
1022                      if(y == 0 || !PieceBlock(selectedPiece, x, y-1, direction, flip))
1023                      {
1024                         surface.foreground = white;
1025                         surface.HLine(drag.x + x * squareWidth-1, drag.x + (x+1) * squareWidth+1, drag.y + y * squareWidth - 1);
1026                         surface.foreground = lime;
1027                         surface.HLine(drag.x + x * squareWidth-2, drag.x + (x+1) * squareWidth+2, drag.y + y * squareWidth - 2);
1028                      }
1029
1030                      if(x == 4 || !PieceBlock(selectedPiece, x+1, y, direction, flip))
1031                      {
1032                         surface.foreground = white;
1033                         surface.VLine(drag.y + y * squareWidth-1, drag.y + (y+1) * squareWidth+1, drag.x + (x+1) * squareWidth + 1);
1034                         surface.foreground = lime;
1035                         surface.VLine(drag.y + y * squareWidth-2, drag.y + (y+1) * squareWidth+2, drag.x + (x+1) * squareWidth + 2);
1036                      }
1037
1038                      if(y == 4 || !PieceBlock(selectedPiece, x, y+1, direction, flip))
1039                      {
1040                         surface.foreground = white;
1041                         surface.HLine(drag.x + x * squareWidth-1, drag.x + (x+1) * squareWidth+1, drag.y + (y+1) * squareWidth + 1);
1042                         surface.foreground = lime;
1043                         surface.HLine(drag.x + x * squareWidth-2, drag.x + (x+1) * squareWidth+2, drag.y + (y+1) * squareWidth + 2);
1044                      }
1045                   }
1046                }
1047          }
1048       }
1049    }
1050
1051    Console chat
1052    {
1053       this, size = { boardSize*squareWidth }, anchor = { left = boardStartX, top = boardStartY + boardSize*squareWidth + 20, bottom = 5 };
1054       font = { "Arial", 11, bold = true };
1055       editTextColor = white;
1056       logTextColor = white;
1057       editHeight = 24;
1058       log.hasVertScroll = bool::true;
1059       log.inactive = bool::true;
1060       visible = false;
1061
1062       bool ProcessCommand(char * command)
1063       {
1064          blokus.server.SendMessage(command);
1065          return false;
1066       }
1067    };
1068
1069    Button btnPass
1070    {
1071       this, caption = "No Move Available! Pass...",
1072       //anchor = { right = 5, bottom = 5 };
1073       anchor = { left = squareWidth * boardSize + 40, bottom = 5 };
1074       inactive = true;
1075       visible = false;
1076
1077       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1078       {
1079          btnPass.visible = false;
1080          blokus.passed[blokus.gameState.colorTurn] = true;
1081          server.Pass();
1082          return true;
1083       }
1084
1085    };
1086 }
1087
1088 class BlokusScores : Window
1089 {
1090    master = blokus;
1091    moveable = true;
1092    borderStyle = fixed;
1093    background = black;
1094    caption = "Blokus Final Scores";
1095    clientSize = { 580, 210 };
1096    font = { "Arial", 12, bold = true };
1097    icon = { ":ollie.png" };
1098    void OnRedraw(Surface surface)
1099    {
1100       PlayerColor p;
1101       char * s;
1102       int len;
1103       char temp[256];
1104       int grandTotals[4];
1105       BlokusGameState * state = &blokus.gameState;
1106       surface.foreground = white;
1107       s = "Score"; len = strlen(s);
1108       surface.WriteText(10, 40, s, len);
1109
1110       s = "Bonus"; len = strlen(s);
1111       surface.WriteText(10, 60, s, len);
1112
1113       s = "Total"; len = strlen(s);
1114       surface.WriteText(10, 100, s, len);
1115
1116       if(state->numPlayers < 3)
1117       {
1118          s = "Grand Total"; len = strlen(s);
1119          surface.WriteText(10, 160, s, len);
1120       }
1121
1122       for(p = blue; p <= green; p++)
1123       {
1124          // TOFIX: bug here, why is -1 required?
1125          int x = 80 + (p-1) * 120;
1126          surface.foreground = colors[1][p];
1127          /* // GCC internal compiler error with -O2, MinGW GCC 4.4.0
1128          s = (state->numPlayers == 3 && p == green) ? "* Green *" : blokus.playerNames[p];
1129          len = strlen(s);
1130          surface.WriteText(x, 20, s, len);
1131          */
1132          if(state->numPlayers == 3 && p == green)
1133          {
1134             s = "* Green *";
1135             len = strlen(s);
1136             surface.WriteText(x, 20, s, len);
1137          }
1138          else if(blokus.playerNames[p])
1139          {
1140             s = blokus.playerNames[p];
1141             len = strlen(s);
1142             surface.WriteText(x, 20, s, len);
1143          }
1144
1145          s = temp; sprintf(temp, "%d", state->scores[p]);
1146          len = strlen(s);
1147          surface.WriteText(x + 30, 40, s, len);
1148
1149          if(state->bonus[p])
1150          {
1151             s = temp; sprintf(temp, "%d", state->bonus[p]);
1152             len = strlen(s);
1153             surface.WriteText(x + 30, 60, s, len);
1154          }
1155
1156          if(state->numPlayers > 2)
1157             grandTotals[p] = state->scores[p] + state->bonus[p];
1158          s = temp; sprintf(temp, "%d", state->scores[p] + state->bonus[p]);
1159          len = strlen(s);
1160          surface.WriteText(x + 30, 100, s, len);
1161
1162          if((state->numPlayers == 2 && p <= yellow) ||
1163             (state->numPlayers == 1 && p == blue))
1164          {
1165             // TOFIX: Annoying +2 conversion issue
1166             if(state->numPlayers == 2)
1167                grandTotals[p] = state->scores[p] + state->bonus[p] +
1168                                 state->scores[p+red /*2*/] + state->bonus[p+red /*2*/];
1169             else
1170                grandTotals[p] = state->scores[0] + state->bonus[0] +
1171                                 state->scores[1] + state->bonus[1] +
1172                                 state->scores[2] + state->bonus[2] +
1173                                 state->scores[3] + state->bonus[3];
1174
1175             if(blokus.playerNames[p])
1176             {
1177                s = blokus.playerNames[p];
1178                len = strlen(s);
1179                surface.WriteText(x, 140, s, len);
1180             }
1181
1182             s = temp; sprintf(temp, "%d", grandTotals[p]);
1183             len = strlen(s);
1184             surface.WriteText(x + 30, 160, s, len);
1185          }
1186       }
1187       if(state->numPlayers > 1)
1188       {
1189          char string[256];
1190          int c, greatest = -MAXINT;
1191          int numTies = 0, ties[3], winner;
1192
1193          string[0] = 0;
1194          for(c = 0; c < state->numPlayers; c++)
1195          {
1196             if(grandTotals[c] > greatest)
1197             {
1198                greatest = grandTotals[c];
1199                numTies = 0;
1200                winner = c;
1201             }
1202             else if(grandTotals[c] == greatest)
1203             {
1204                if(!numTies)
1205                {
1206                   numTies = 1;
1207                   ties[0] = winner;
1208                }
1209                ties[numTies++] = c;
1210             }
1211          }
1212          if(numTies)
1213          {
1214             for(c = 0; c < numTies; c++)
1215             {
1216                strcat(string, blokus.playerNames[c]);
1217                if(c < numTies-2)
1218                   strcat(string, ", ");
1219                else if(c < numTies-1)
1220                   strcat(string, " and ");
1221             }
1222             surface.foreground = white;
1223             strcat(string, " tied!");
1224          }
1225          else
1226          {
1227             surface.foreground = colors[1][(PlayerColor)winner];
1228             sprintf(string, "%s won!", blokus.playerNames[winner]);
1229          }
1230
1231          len = strlen(string);
1232          surface.WriteText(100, 180, string, strlen(string));
1233       }
1234    }
1235 }
1236
1237 BlokusScores scoresPanel { visible = false };
1238
1239 Blokus blokus { };
1240
1241 class BlokusApp : GuiApplication
1242 {
1243    //fullScreen = true;
1244    bool Init()
1245    {
1246       blokus.Create();
1247       panel.Create();
1248       return true;
1249    }
1250
1251    bool Cycle(bool idle)
1252    {
1253       // This is here because it hangs in MovePlayed() (Why?)
1254       scoresPanel.visible = blokus.gameStarted && blokus.gameState.over;
1255 #ifdef _DEBUG
1256       /*if(blokus.gameState.over)
1257          panel.btnStart.NotifyClicked(panel, panel.btnStart, 0, 0, 0);*/
1258 #endif
1259       return true;
1260    }
1261    void Terminate()
1262    {
1263       if(hosting)
1264          blokusService.Stop();
1265    }
1266 }
1267
1268 import remote "BlokusServer"
1269
1270 define app = ((BlokusApp)__thisModule.application);
1271
1272 define BLOKUS_PORT = 1495;
1273 static bool hosting;
1274 define MaxPlayers = 4;
1275
1276 class CommunicationPanel : Window
1277 {
1278    caption = "Blokus Communication Panel";
1279    background = lightSlateGray;
1280    borderStyle = fixed;
1281    hasClose = true;
1282    tabCycle = true;
1283    clientSize = { 430, 300 };
1284 #ifdef __ANDROID__
1285    anchor = { right = 0, bottom = 0 };
1286 #endif
1287    icon = { ":ollie.png" };
1288
1289    // Other player info
1290    char playerNames[MaxPlayers][256];
1291
1292    DataField fldName { header = "Name", width = 100 };
1293    DataField fldAddr { header = "Address" };
1294
1295    bool OnClose(bool parentClosing)
1296    {
1297       if(!blokus || blokus.destroyed || blokus.Destroy(0))
1298          return true;
1299       return false;
1300    }
1301
1302    void OnDestroy()
1303    {
1304       app.Unlock();
1305       if(hosting)
1306          blokusService.Stop();
1307       app.Lock();
1308    }
1309
1310    CommunicationPanel()
1311    {
1312       listPlayers.AddField(fldName);
1313       listPlayers.AddField(fldAddr);
1314    }
1315
1316    void ListPlayers()
1317    {
1318       int c;
1319       listPlayers.Clear();
1320       fldAddr.header = hosting ? "Address" : "";
1321       for(c = 0; c<MaxPlayers; c++)
1322       {
1323          if(hosting)
1324          {
1325             if(serverPlayers[c])
1326             {
1327                DataRow row = listPlayers.AddRow();
1328                DCOMServerObject object = (DCOMServerObject)serverPlayers[c].connection._vTbl[-1];
1329
1330                row.tag = serverPlayers[c].id;
1331                row.SetData(fldName, serverPlayers[c].name);
1332                row.SetData(fldAddr, object.serverSocket.inetAddress);
1333             }
1334          }
1335          else if(playerNames[c][0])
1336          {
1337             DataRow row = listPlayers.AddRow();
1338             row.SetData(fldName, playerNames[c]);
1339          }
1340       }
1341    }
1342
1343    void UpdateControlsStates()
1344    {
1345       int numPlayers = 0;
1346       if(hosting)
1347       {
1348          int c;
1349          for(c = 0; c<MaxPlayers; c++)
1350             if(serverPlayers[c])
1351                numPlayers++;
1352       }
1353       lblServerAddress.disabled = serverAddress.disabled = blokus.server ? true : false;
1354       lblPlayerName.disabled = playerName.disabled = blokus.server ? true : false;
1355       lblServerAddress.Update(null);
1356       lblPlayerName.Update(null);
1357       btnConnect.visible = blokus.server ? false : true;
1358       btnDisconnect.visible = blokus.server ? true : false;
1359       blokus.chat.visible = blokus.server ? true : false;
1360
1361       btnHost.visible = !hosting && !blokus.server;
1362       btnStopHosting.visible = hosting;
1363       btnStart.visible = hosting && (!serverGameStarted || serverGameState.over) && numPlayers > 0;
1364       btnStopGame.visible = hosting && (serverGameStarted && !serverGameState.over);
1365       listPlayers.visible = (hosting && (serverGameStarted || numPlayers > 0)) || (!hosting && blokus.server && blokus.gameStarted);
1366       btnKick.visible = hosting && !serverGameStarted && numPlayers > 0;
1367       btnKick.disabled = listPlayers.currentRow ? false : true;
1368    }
1369    EditBox serverAddress
1370    {
1371       this, caption = "Server Address:", altA, font = { "Tahoma", 10, bold = true }, size = { 220, 24 }, position = { 16, 64 }, contents = "localhost"
1372    };
1373    Label lblServerAddress { this, font = { "Tahoma", 8.25f, bold = true }, position = { 16, 40 }, labeledWindow = serverAddress };
1374    Button btnConnect
1375    {
1376       this, caption = "Connect", altC, isDefault = true, font = { "Arial", 16, bold = true }, size = { 126, 32 }, position = { 256, 64 };
1377
1378       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1379       {
1380          if(!blokus.server)
1381          {
1382             blokus.server = ServerConnection
1383             {
1384                void OnDisconnect(int code)
1385                {
1386                   app.Lock();
1387                   if(blokus)
1388                   {
1389                      delete blokus.server;
1390                      blokus.gameStarted = false;
1391                      blokus.turnLightTimer.Stop();
1392                      blokus.lightValue = 1;
1393                      blokus.lightDir = -.1f;
1394                      blokus.Update(null);
1395                      panel.UpdateControlsStates();
1396                      panel.ListPlayers();
1397                   }
1398                   DCOMClientObject::OnDisconnect(code);
1399                   app.Unlock();
1400                }
1401
1402                void GameStarted(GameInfo gameInfo)
1403                {
1404                   int x,y;
1405                   int c, np = 0;
1406
1407                   blokus.gameState.numPlayers = gameInfo.numPlayers;
1408                   blokus.firstColor = gameInfo.firstColor;
1409                   blokus.colorPlayed = blokus.firstColor;
1410
1411                   for(c = 0; c<MaxPlayers; c++)
1412                   {
1413                      if(gameInfo.players[c][0])
1414                         strcpy(panel.playerNames[np++], gameInfo.players[c]);
1415                   }
1416
1417                   blokus.btnPass.visible = false;
1418                   blokus.gameState.NewGame();
1419                   blokus.passed[0] = false;
1420                   blokus.passed[1] = false;
1421                   blokus.passed[2] = false;
1422                   blokus.passed[3] = false;
1423                   blokus.gameStarted = true;
1424                   blokus.lightValue = 1;
1425                   blokus.lightDir = -.1f;
1426                   blokus.turnLightTimer.Start();
1427                   blokus.UpdatePlayerNames();
1428
1429                   blokus.Update(null);
1430
1431                   panel.UpdateControlsStates();
1432                   panel.ListPlayers();
1433                }
1434
1435                void GameEnded()
1436                {
1437                   int c;
1438
1439                   panel.ListPlayers();
1440                   blokus.gameStarted = false;
1441                   blokus.btnPass.visible = false;
1442                   blokus.Update(null);
1443                }
1444
1445                void MovePlayed(PlayerColor color, int pieceType, int direction, bool flip, int boardX, int boardY)
1446                {
1447                   blokus.gameState.PlayMove(pieceType, direction, flip, boardX, boardY);
1448                   if(color == blokus.colorPlayed)
1449                      blokus.NextColorPlayed();
1450 #ifdef _DEBUG
1451                   blokus.gotMove = true;
1452 #endif
1453                   if(blokus.colorPlayed == blokus.gameState.colorTurn && !blokus.gameState.over)
1454                   {
1455                      if(!blokus.gameState.validMove)
1456                      {
1457                         if(!blokus.passed[blokus.gameState.colorTurn])
1458                         {
1459                            blokus.btnPass.visible = true;
1460                            if(!blokus.active)
1461                               blokus.Flash();
1462                         }
1463                         else
1464                            blokus.server.Pass();
1465                      }
1466                      else if(!blokus.active)
1467                         blokus.Flash();
1468                   }
1469
1470                   // This hangs here, why?
1471                   /*if(blokus.gameState.over)
1472                      scoresPanel.visible = true;*/
1473                   if(blokus)
1474                   {
1475                      blokus.UpdatePlayerNames();
1476                      blokus.Update(null);
1477                   }
1478                }
1479
1480                void Passed(PlayerColor color)
1481                {
1482                   blokus.gameState.Pass();
1483                   if(color == blokus.colorPlayed)
1484                      blokus.NextColorPlayed();
1485                   else if(!blokus.active)
1486                      blokus.Flash();
1487
1488                   if(blokus.colorPlayed == blokus.gameState.colorTurn && !blokus.gameState.over)
1489                   {
1490                      if(!blokus.gameState.validMove)
1491                      {
1492                         if(!blokus.passed[blokus.gameState.colorTurn])
1493                         {
1494                            blokus.btnPass.visible = true;
1495                            if(!blokus.active)
1496                               blokus.Flash();
1497                         }
1498                         else
1499                            blokus.server.Pass();
1500                      }
1501                      else if(!blokus.active)
1502                         blokus.Flash();
1503                   }
1504
1505                   if(blokus)
1506                   {
1507                      blokus.UpdatePlayerNames();
1508                      blokus.Update(null);
1509                   }
1510                }
1511
1512                void NotifyMessage(String name, String msg)
1513                {
1514                   EditBox log = blokus.chat.log;
1515                   char * format = (log.numLines > 1 || log.line.count) ?
1516                      "\n%s: %s" : "%s: %s";
1517                   int len = strlen(msg);
1518                   // Avoid buffer overflow...
1519                   if(len >= MAX_F_STRING-100)
1520                      msg[MAX_F_STRING-100] = 0;
1521                   blokus.chat.Log(format, name, msg);
1522                }
1523             };
1524             incref blokus.server;
1525             if(blokus.server.Connect(serverAddress.contents, BLOKUS_PORT))
1526             {
1527                int playerID = blokus.server.Join();
1528                if(blokus.server && playerID != -1)
1529                   blokus.server.SetName(playerName.contents);
1530                else
1531                   blokus.server.Disconnect(0);
1532                UpdateControlsStates();
1533             }
1534             else
1535                delete blokus.server;
1536          }
1537          return true;
1538       }
1539    };
1540    Button btnDisconnect
1541    {
1542       this, caption = "Disconnect", altD, font = { "Arial", 16, bold = true }, size = { 126, 32 }, position = { 256, 64 }, visible = false;
1543
1544       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1545       {
1546          if(!blokus.gameStarted || blokus.gameState.over ||
1547             MessageBox { type = okCancel, caption = "Ecere Blokus",
1548                contents = "Game in progress! Disconnect?"
1549             }.Modal() == ok)
1550          {
1551             if(blokus.server)
1552                blokus.server.Disconnect(0);
1553          }
1554          return true;
1555       }
1556    };
1557    Button btnHost
1558    {
1559       this, caption = "Host", altH, font = { "Arial", 16, bold = true }, size = { 90, 32 }, position = { 16, 112 };
1560
1561       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1562       {
1563          if(!blokus.gameStarted)
1564          {
1565             if(blokusService.Start())
1566             {
1567                hosting = true;
1568                Update(null);
1569                UpdateControlsStates();
1570             }
1571          }
1572          return true;
1573       }
1574    };
1575    Button btnStopHosting
1576    {
1577       this, caption = "Stop Hosting", altP, font = { "Arial", 16, bold = true }, position = { 16, 112 }, visible = false;
1578
1579       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1580       {
1581          int numPlayers = 0;
1582          int c;
1583          for(c = 0; c<MaxPlayers; c++)
1584             if(serverPlayers[c])
1585                numPlayers++;
1586          if(!numPlayers ||
1587             MessageBox { type = okCancel, caption = "Ecere Blokus",
1588                contents = "Players connected! Stop hosting?"
1589             }.Modal() == ok)
1590          {
1591             app.Unlock();
1592             blokusService.Stop();
1593             app.Lock();
1594             hosting = false;
1595             Update(null);
1596             UpdateControlsStates();
1597          }
1598          return true;
1599       }
1600    };
1601    Button btnStart
1602    {
1603       this, caption = "Start Game", altS, font = { "Arial", 16, bold = true }, size = { 124, 32 }, position = { 256, 112 }, visible = false;
1604
1605       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1606       {
1607          if(hosting)
1608          {
1609             StartGame();
1610             UpdateControlsStates();
1611          }
1612          return true;
1613       }
1614    };
1615    Button btnStopGame
1616    {
1617       this, caption = "Stop Game", altG, font = { "Arial", 16, bold = true }, position = { 256, 112 }, visible = false;
1618
1619       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1620       {
1621          if(!serverGameStarted || serverGameState.over ||
1622             MessageBox { type = okCancel, caption = "Ecere Blokus",
1623                contents = "Stop game in progress?"
1624             }.Modal() == ok)
1625          {
1626             EndGame();
1627             UpdateControlsStates();
1628          }
1629          return true;
1630       }
1631    };
1632    ListBox listPlayers
1633    {
1634       this, caption = "Players Connected", altD, size = { 236, 84 }, position = { 16, 176 }, visible = false, hasHeader = true;
1635
1636       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
1637       {
1638          UpdateControlsStates();
1639          return true;
1640       }
1641    };
1642    Label lblListPlayers { this, font = { "Tahoma", 8.25f, bold = true }, position = { 16, 152 }, visible = false, labeledWindow = listPlayers };
1643    Button btnKick
1644    {
1645       this, caption = "Kick", altK, font = { "Arial", 16, bold = true }, size = { 80, 32 }, position = { 264, 224 }, visible = false;
1646
1647       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1648       {
1649          DataRow row = listPlayers.currentRow;
1650          if(row)
1651          {
1652             int id = (int)row.tag;
1653             char msg[1024];
1654             sprintf(msg, "Kick %s?", serverPlayers[id].name);
1655             if(MessageBox { type = okCancel, caption = "Ecere Blokus",
1656                   contents = msg
1657                }.Modal() == ok)
1658                KickPlayer(id);
1659          }
1660          return true;
1661       }
1662    };
1663    EditBox playerName
1664    {
1665       this, caption = "Player Name:", altN, font = { "Arial", 12 }, size = { 132, 24 }, position = { 104, 8 }, contents = "BlokusPlayer"
1666    };
1667    Label lblPlayerName { this, font = { "Tahoma", 8.25f, bold = true }, position = { 16, 16 }, labeledWindow = playerName };
1668 }
1669
1670 CommunicationPanel panel { };
1671
1672 DCOMService blokusService { port = BLOKUS_PORT };