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