compiler/libec: (#341, #351, #644, #771) Improved enum type matching and type handlin...
[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                                        validMove = true;
521                                     }
522                                  }
523                               }
524                            }
525                         }
526                         break;
527                      }
528                   }
529                }
530             }
531          }
532          return true;
533       }
534    };
535    bool gotMove;
536    gotMove = true;
537 #endif
538
539 #ifdef __WIN32__
540    // To work around Flash() lockups on Windows...
541    bool flash;
542    Timer flashTimer
543    {
544       this, delay = 0.2, true;
545
546       bool DelayExpired()
547       {
548          if(flash)
549             Window::Flash();
550          flash = false;
551          return true;
552       }
553    };
554
555    void Flash()
556    {
557       flash = true;
558    }
559 #endif
560
561    void NextColorPlayed()
562    {
563       if(gameState.numPlayers == 1)
564       {
565          if(++colorPlayed > green) colorPlayed = 0;
566       }
567       else if(gameState.numPlayers == 2)
568       {
569          if((colorPlayed+=2) > green) colorPlayed = firstColor;
570       }
571       else if(gameState.numPlayers == 3)
572       {
573          colorPlayed = (colorPlayed == firstColor && gameState.rotatingColor == firstColor) ? green : firstColor;
574       }
575    }
576
577    void UpdatePlayerNames()
578    {
579       int c;
580       if(gameState.numPlayers == 1)
581       {
582          for(c = 0; c < 4; c++)
583             playerNames[c] = panel.playerNames[0];
584       }
585       else if(gameState.numPlayers == 2)
586       {
587          for(c = 0; c < 2; c++)
588          {
589             playerNames[c] = panel.playerNames[c];
590             playerNames[c+2] = panel.playerNames[c];
591          }
592       }
593       else if(gameState.numPlayers == 3)
594       {
595          for(c = 0; c < 3; c++)
596             playerNames[c] = panel.playerNames[c];
597          playerNames[3] = panel.playerNames[gameState.rotatingColor];
598       }
599       else if(gameState.numPlayers == 4)
600       {
601          for(c = 0; c < 4; c++)
602             playerNames[c] = panel.playerNames[c];
603       }
604    }
605
606    void DrawSquare(Surface surface, int x, int y, CornerBlocksColor color, int shade)
607    {
608       surface.background = colors[shade][color];
609       surface.Area(x+1, y+1, x + squareWidth-1, y + squareWidth-1);
610       surface.foreground = lightGray;
611       surface.VLine(y+blockOffset1, y + blockOffset2, x + blockOffset1);
612       surface.HLine(x+blockOffset1, x + blockOffset3, y + blockOffset1);
613       surface.foreground = white;
614       surface.Rectangle(x + blockOffset0,y+blockOffset0, x + squareWidth-blockOffset0, y + squareWidth - blockOffset0);
615    }
616
617    bool OnClose(bool parentClosing)
618    {
619       if((gameStarted && !gameState.over) || hosting)
620       {
621          if(MessageBox { type = okCancel,
622             caption = "Ecere CornerBlocks", contents = "Quit Ecere CornerBlocks?" }.Modal() == cancel)
623             return false;
624       }
625       return true;
626    }
627
628    void OnDestroy()
629    {
630       if(server)
631          server.Disconnect(0);
632
633       panel.Destroy(0);
634       scoresPanel.Destroy(0);
635    }
636
637    ~CornerBlocks()
638    {
639       delete server;
640    }
641
642    bool OnMouseMove(int mx, int my, Modifiers mods)
643    {
644       if(dragging)
645       {
646          Piece * piece = &pieces[selectedPiece];
647          int w = (direction & 1) ? piece->h : piece->w;
648          int h = (direction & 1) ? piece->w : piece->h;
649          drag = { offset.x + mx, offset.y + my };
650
651          if(mx - squareDragged.x * squareWidth >= boardStartX - 10 && mx - squareDragged.x * squareWidth < boardStartX + ((boardSize-w)+1) * squareWidth + 10 &&
652             my - squareDragged.y * squareWidth >= boardStartY - 10 && my - squareDragged.y * squareWidth < boardStartY + ((boardSize-h)+1) * squareWidth + 10)
653          {
654             int x, y;
655             x = Max(0,mx - squareDragged.x * squareWidth - boardStartX) / squareWidth;
656             y = Max(0,my - squareDragged.y * squareWidth - boardStartY) / squareWidth;
657             x = Min(x, boardSize-w);
658             y = Min(y, boardSize-h);
659             drag.x = boardStartX + x * squareWidth;
660             drag.y = boardStartY + y * squareWidth;
661             boardPos = { x, y };
662             onBoard = true;
663          }
664          else
665             onBoard = false;
666          Update(null);
667       }
668       return true;
669    }
670
671    bool OnLeftButtonUp(int mx, int my, Modifiers mods)
672    {
673       if(dragging)
674       {
675          if(onBoard)
676          {
677             if(gameStarted && colorPlayed == gameState.colorTurn)
678             {
679                Piece * piece = &pieces[selectedPiece];
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 rx, ry;
738          int x = squareDragged.x, y = squareDragged.y;
739          int w,h;
740          bool isDown = key == wheelDown || key == right || key == down;
741
742          if(isDown)
743          {
744             if(++direction == 4) direction = 0;
745          }
746          else
747          {
748             if(--direction == -1) direction = 3;
749          }
750          w = (direction & 1) ? piece->h : piece->w;
751          h = (direction & 1) ? piece->w : piece->h;
752
753          offset.x += squareDragged.x * squareWidth;
754          offset.y += squareDragged.y * squareWidth;
755          if(isDown)
756             squareDragged = { w-1-y, x };
757          else
758             squareDragged = { y, h-1-x };
759
760          offset.x -= squareDragged.x * squareWidth;
761          offset.y -= squareDragged.y * squareWidth;
762
763          OnMouseMove(mx,my,0);
764
765          Update(null);
766       }
767       return true;
768    }
769
770    bool OnLeftButtonDown(int mx, int my, Modifiers mods)
771    {
772       if( mx > squareWidth * boardSize + 40)
773       {
774          int bx, by;
775          bool selected = false;
776          int pieceNum = 0;
777          Point offset;
778          int rh = 0;
779          int c;
780          // Draw Player Pieces
781          bx = squareWidth * boardSize + 40;
782          by = piecesY;
783          for(c = 0; c < numPieces; c++)
784          {
785             if(gameState.playerPieces[colorPlayed][c])
786             {
787                int w = pieces[c].w, h = pieces[c].h;
788                int y;
789                for(y = 0; y < 5; y++)
790                {
791                   int x;
792                   for(x = 0; x < 5; x++)
793                   {
794                      if(PieceBlock(c, x, y, 0, false))
795                      {
796                         int dx = bx + x * squareWidth;
797                         int dy = by + y * squareWidth;
798                         if(mx >= dx && mx < dx + squareWidth &&
799                            my >= dy && my < dy + squareWidth)
800                         {
801                            selected = true;
802                            pieceNum = c;
803                            offset = { bx - mx, by - my };
804                            squareDragged = { x, y };
805                            break;
806                         }
807                      }
808                      if(selected) break;
809                   }
810                   if(selected) break;
811                }
812                if(selected) break;
813                bx += w * squareWidth + squareWidth;
814                if(h > rh) rh = h;
815                if(bx + 5 * squareWidth > clientSize.w)
816                {
817                   bx = squareWidth * boardSize + 40;
818                   by += (rh+1) * squareWidth;
819                   rh = 0;
820                }
821             }
822          }
823          if(selected)
824          {
825             Update(null);
826             direction = 0;
827             flip = 0;
828             selectedPiece = pieceNum;
829             this.offset = offset;
830             dragging = true;
831             OnMouseMove(mx, my, mods);
832             Capture();
833          }
834       }
835       return true;
836    }
837
838    float lightValue, lightDir;
839    lightValue = 1;
840    lightDir = -.1f;
841    Timer turnLightTimer
842    {
843       this, 0.1, false;
844
845       bool DelayExpired()
846       {
847          lightValue += lightDir;
848          if(lightValue < 0) { lightValue = 0; lightDir = .1f; }
849          if(lightValue > 1) { lightValue = 1; lightDir =-.1f; }
850          Update(null);
851          return true;
852       }
853    };
854
855    void OnRedraw(Surface surface)
856    {
857       int c;
858       int bx = boardStartX;
859       int by = boardStartY;
860       int x, y;
861       char * s;
862       int len, tw;
863       Color turnLight = white;
864       if(gameStarted && !gameState.over)
865       {
866          ColorRGB empty = colors[1][gameState.colorTurn] /*gray*/, full = white;
867          turnLight = ColorRGB
868          {
869             empty.r + lightValue * (full.r - empty.r),
870             empty.g + lightValue * (full.g - empty.g),
871             empty.b + lightValue * (full.b - empty.b)
872          };
873       }
874
875       surface.foreground = aqua;
876       for(c = 0; c <= boardSize; c++)
877       {
878          surface.HLine(bx,bx+squareWidth*boardSize, by + c * squareWidth);
879          surface.VLine(by,by+squareWidth*boardSize, bx + c * squareWidth);
880       }
881
882       surface.background = colors[cornerBlocks.gameStarted][blue];
883       x = bx;
884       y = by;
885       surface.Area(x - 10, y - 10, x + 10, y-1);
886       surface.Area(x - 10, y - 10, x - 1, y+10);
887
888       s = playerNames[PlayerColor::blue];
889       if(s)
890       {
891          len = strlen(s);
892          surface.foreground = (gameState.colorTurn == blue) ? turnLight : white;
893          surface.WriteText(x + 15, y - 20, s, len);
894       }
895
896       surface.background = colors[cornerBlocks.gameStarted][yellow];
897       x = bx + boardSize*squareWidth;
898       y = by;
899       surface.Area(x - 10, y - 10, x + 10, y-1);
900       surface.Area(x + 1, y - 10, x + 10,  y+10);
901
902       s = playerNames[PlayerColor::yellow];
903       if(s)
904       {
905          len = strlen(s);
906          surface.TextExtent(s, len, &tw, null);
907          surface.foreground = (gameState.colorTurn == yellow) ? turnLight : white;
908          surface.WriteText(x - 15 - tw, y - 20, s, len);
909       }
910
911       surface.background = colors[cornerBlocks.gameStarted][red];
912       x = bx + boardSize*squareWidth;
913       y = by + boardSize*squareWidth;
914       surface.Area(x - 10, y + 1, x + 10, y+10);
915       surface.Area(x + 1, y - 10, x + 10,  y+10);
916       s = playerNames[PlayerColor::red];
917       if(s)
918       {
919          len = strlen(s);
920          surface.TextExtent(s, len, &tw, null);
921          surface.foreground = (gameState.colorTurn == red) ? turnLight : white;
922          surface.WriteText(x - 15 - tw, y, s, len);
923       }
924
925       surface.background = colors[cornerBlocks.gameStarted][green];
926       x = bx;
927       y = by + boardSize*squareWidth;
928       surface.Area(x - 10, y + 1, x + 10, y+10);
929       surface.Area(x - 10, y - 10, x - 1,  y+10);
930       s = playerNames[PlayerColor::green];
931       if(s)
932       {
933          len = strlen(s);
934          surface.foreground = (gameState.colorTurn == green) ? turnLight : white;
935          surface.WriteText(x + 15, y, s, len);
936       }
937
938       if(gameState.over)
939       {
940          surface.font = yourTurnFont.font;
941          surface.foreground = crimson;
942          surface.CenterTextf(x + boardSize*squareWidth/2, y + 3, "Game Over");
943       }
944       else if(gameState.numPlayers > 1 && gameStarted && colorPlayed == gameState.colorTurn)
945       {
946          surface.font = yourTurnFont.font;
947          surface.foreground = tomato;
948          surface.CenterTextf(x + boardSize*squareWidth/2, y + 3, "Your turn");
949       }
950
951       for(y = 0; y < boardSize; y++)
952       {
953          for(x = 0; x < boardSize; x++)
954          {
955             CornerBlocksColor color = gameState.board[y * boardSize + x];
956             if(color)
957             {
958                DrawSquare(surface, bx + x * squareWidth, by + y * squareWidth, color, cornerBlocks.gameStarted);
959             }
960          }
961       }
962
963       {
964          int rh = 0;
965          // Draw Player Pieces
966          bx = squareWidth * boardSize + 40;
967          by = piecesY;
968          for(c = 0; c < numPieces; c++)
969          {
970             if(gameState.playerPieces[colorPlayed][c])
971             {
972                int w = pieces[c].w, h = pieces[c].h;
973                for(y = 0; y < 5; y++)
974                {
975                   for(x = 0; x < 5; x++)
976                   {
977                      if(PieceBlock(c, x, y, 0, false))
978                      {
979                         if(!dragging || selectedPiece != c)
980                            DrawSquare(surface,
981                               bx + x * squareWidth,
982                               by + y * squareWidth,
983                               colorPlayed,
984                               gameStarted && (gameState.colorTurn == colorPlayed && gameState.validPieces[c]));
985                      }
986                   }
987                }
988                if(h > rh) rh = h;
989                bx += w * squareWidth + squareWidth;
990                if(bx + 5 * squareWidth > clientSize.w)
991                {
992                   bx = squareWidth * boardSize + 40;
993                   by += rh * squareWidth + squareWidth;
994                   rh = 0;
995                }
996             }
997          }
998          // Draw Dragged piece
999          if(dragging)
1000          {
1001             for(y = 0; y < 5; y++)
1002                for(x = 0; x < 5; x++)
1003                {
1004                   if(PieceBlock(selectedPiece, x, y, direction, flip))
1005                   {
1006                      DrawSquare(surface,
1007                         drag.x + x * squareWidth,
1008                         drag.y + y * squareWidth,
1009                         colorPlayed,
1010                         gameStarted && gameState.colorTurn == colorPlayed && gameState.validPieces[selectedPiece]);
1011
1012                      if(x == 0 || !PieceBlock(selectedPiece, x-1, y, direction, flip))
1013                      {
1014                         surface.foreground = white;
1015                         surface.VLine(drag.y + y * squareWidth-1, drag.y + (y+1) * squareWidth+1, drag.x + x * squareWidth - 1);
1016                         surface.foreground = lime;
1017                         surface.VLine(drag.y + y * squareWidth-2, drag.y + (y+1) * squareWidth+2, drag.x + x * squareWidth - 2);
1018                      }
1019
1020                      if(y == 0 || !PieceBlock(selectedPiece, x, y-1, direction, flip))
1021                      {
1022                         surface.foreground = white;
1023                         surface.HLine(drag.x + x * squareWidth-1, drag.x + (x+1) * squareWidth+1, drag.y + y * squareWidth - 1);
1024                         surface.foreground = lime;
1025                         surface.HLine(drag.x + x * squareWidth-2, drag.x + (x+1) * squareWidth+2, drag.y + y * squareWidth - 2);
1026                      }
1027
1028                      if(x == 4 || !PieceBlock(selectedPiece, x+1, y, direction, flip))
1029                      {
1030                         surface.foreground = white;
1031                         surface.VLine(drag.y + y * squareWidth-1, drag.y + (y+1) * squareWidth+1, drag.x + (x+1) * squareWidth + 1);
1032                         surface.foreground = lime;
1033                         surface.VLine(drag.y + y * squareWidth-2, drag.y + (y+1) * squareWidth+2, drag.x + (x+1) * squareWidth + 2);
1034                      }
1035
1036                      if(y == 4 || !PieceBlock(selectedPiece, x, y+1, direction, flip))
1037                      {
1038                         surface.foreground = white;
1039                         surface.HLine(drag.x + x * squareWidth-1, drag.x + (x+1) * squareWidth+1, drag.y + (y+1) * squareWidth + 1);
1040                         surface.foreground = lime;
1041                         surface.HLine(drag.x + x * squareWidth-2, drag.x + (x+1) * squareWidth+2, drag.y + (y+1) * squareWidth + 2);
1042                      }
1043                   }
1044                }
1045          }
1046       }
1047    }
1048
1049    Console chat
1050    {
1051       this, size = { boardSize*squareWidth }, anchor = { left = boardStartX, top = boardStartY + boardSize*squareWidth + 20, bottom = 5 };
1052       font = { "Arial", 11, bold = true };
1053       editTextColor = white;
1054       logTextColor = white;
1055       editHeight = 24;
1056       log.hasVertScroll = bool::true;
1057       log.inactive = bool::true;
1058       visible = false;
1059
1060       bool ProcessCommand(char * command)
1061       {
1062          cornerBlocks.server.SendMessage(command);
1063          return false;
1064       }
1065    };
1066
1067    Button btnPass
1068    {
1069       this, caption = "No Move Available! Pass...",
1070       //anchor = { right = 5, bottom = 5 };
1071       anchor = { left = squareWidth * boardSize + 40, bottom = 5 };
1072       inactive = true;
1073       visible = false;
1074
1075       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1076       {
1077          btnPass.visible = false;
1078          cornerBlocks.passed[cornerBlocks.gameState.colorTurn] = true;
1079          server.Pass();
1080          return true;
1081       }
1082
1083    };
1084 }
1085
1086 class CornerBlocksScores : Window
1087 {
1088    master = cornerBlocks;
1089    moveable = true;
1090    borderStyle = fixed;
1091    background = black;
1092    caption = "CornerBlocks Final Scores";
1093    clientSize = { 580, 210 };
1094    font = { "Arial", 12, bold = true };
1095    icon = { ":ollie.png" };
1096    void OnRedraw(Surface surface)
1097    {
1098       PlayerColor p;
1099       char * s;
1100       int len;
1101       char temp[256];
1102       int grandTotals[4];
1103       CornerBlocksGameState * state = &cornerBlocks.gameState;
1104       surface.foreground = white;
1105       s = "Score"; len = strlen(s);
1106       surface.WriteText(10, 40, s, len);
1107
1108       s = "Bonus"; len = strlen(s);
1109       surface.WriteText(10, 60, s, len);
1110
1111       s = "Total"; len = strlen(s);
1112       surface.WriteText(10, 100, s, len);
1113
1114       if(state->numPlayers < 3)
1115       {
1116          s = "Grand Total"; len = strlen(s);
1117          surface.WriteText(10, 160, s, len);
1118       }
1119
1120       for(p = blue; p <= green; p++)
1121       {
1122          int x = 80 + p * 120;
1123          surface.foreground = colors[1][p];
1124          /* // GCC internal compiler error with -O2, MinGW GCC 4.4.0
1125          s = (state->numPlayers == 3 && p == green) ? "* Green *" : cornerBlocks.playerNames[p];
1126          len = strlen(s);
1127          surface.WriteText(x, 20, s, len);
1128          */
1129          if(state->numPlayers == 3 && p == green)
1130          {
1131             s = "* Green *";
1132             len = strlen(s);
1133             surface.WriteText(x, 20, s, len);
1134          }
1135          else if(cornerBlocks.playerNames[p])
1136          {
1137             s = cornerBlocks.playerNames[p];
1138             len = strlen(s);
1139             surface.WriteText(x, 20, s, len);
1140          }
1141
1142          s = temp; sprintf(temp, "%d", state->scores[p]);
1143          len = strlen(s);
1144          surface.WriteText(x + 30, 40, s, len);
1145
1146          if(state->bonus[p])
1147          {
1148             s = temp; sprintf(temp, "%d", state->bonus[p]);
1149             len = strlen(s);
1150             surface.WriteText(x + 30, 60, s, len);
1151          }
1152
1153          if(state->numPlayers > 2)
1154             grandTotals[p] = state->scores[p] + state->bonus[p];
1155          s = temp; sprintf(temp, "%d", state->scores[p] + state->bonus[p]);
1156          len = strlen(s);
1157          surface.WriteText(x + 30, 100, s, len);
1158
1159          if((state->numPlayers == 2 && p <= yellow) ||
1160             (state->numPlayers == 1 && p == blue))
1161          {
1162             if(state->numPlayers == 2)
1163                grandTotals[p] = state->scores[p] + state->bonus[p] +
1164                                 state->scores[p+2] + state->bonus[p+2];
1165             else
1166                grandTotals[p] = state->scores[0] + state->bonus[0] +
1167                                 state->scores[1] + state->bonus[1] +
1168                                 state->scores[2] + state->bonus[2] +
1169                                 state->scores[3] + state->bonus[3];
1170
1171             if(cornerBlocks.playerNames[p])
1172             {
1173                s = cornerBlocks.playerNames[p];
1174                len = strlen(s);
1175                surface.WriteText(x, 140, s, len);
1176             }
1177
1178             s = temp; sprintf(temp, "%d", grandTotals[p]);
1179             len = strlen(s);
1180             surface.WriteText(x + 30, 160, s, len);
1181          }
1182       }
1183       if(state->numPlayers > 1)
1184       {
1185          char string[256];
1186          int c, greatest = -MAXINT;
1187          int numTies = 0, ties[3], winner;
1188
1189          string[0] = 0;
1190          for(c = 0; c < state->numPlayers; c++)
1191          {
1192             if(grandTotals[c] > greatest)
1193             {
1194                greatest = grandTotals[c];
1195                numTies = 0;
1196                winner = c;
1197             }
1198             else if(grandTotals[c] == greatest)
1199             {
1200                if(!numTies)
1201                {
1202                   numTies = 1;
1203                   ties[0] = winner;
1204                }
1205                ties[numTies++] = c;
1206             }
1207          }
1208          if(numTies)
1209          {
1210             for(c = 0; c < numTies; c++)
1211             {
1212                strcat(string, cornerBlocks.playerNames[c]);
1213                if(c < numTies-2)
1214                   strcat(string, ", ");
1215                else if(c < numTies-1)
1216                   strcat(string, " and ");
1217             }
1218             surface.foreground = white;
1219             strcat(string, " tied!");
1220          }
1221          else
1222          {
1223             surface.foreground = colors[1][(PlayerColor)winner];
1224             sprintf(string, "%s won!", cornerBlocks.playerNames[winner]);
1225          }
1226
1227          len = strlen(string);
1228          surface.WriteText(100, 180, string, strlen(string));
1229       }
1230    }
1231 }
1232
1233 CornerBlocksScores scoresPanel { visible = false };
1234
1235 CornerBlocks cornerBlocks { };
1236
1237 class CornerBlocksApp : GuiApplication
1238 {
1239    //fullScreen = true;
1240    bool Init()
1241    {
1242       cornerBlocks.Create();
1243       panel.Create();
1244       return true;
1245    }
1246
1247    bool Cycle(bool idle)
1248    {
1249       // This is here because it hangs in MovePlayed() (Why?)
1250       scoresPanel.visible = cornerBlocks.gameStarted && cornerBlocks.gameState.over;
1251 #ifdef _DEBUG
1252       /*if(cornerBlocks.gameState.over)
1253          panel.btnStart.NotifyClicked(panel, panel.btnStart, 0, 0, 0);*/
1254 #endif
1255       return true;
1256    }
1257    void Terminate()
1258    {
1259       if(hosting)
1260          cornerBlocksService.Stop();
1261    }
1262 }
1263
1264 import remote "CornerBlocksServer"
1265
1266 define app = ((CornerBlocksApp)__thisModule.application);
1267
1268 define CORNERBLOCKS_PORT = 1495;
1269 static bool hosting;
1270 define MaxPlayers = 4;
1271
1272 class CommunicationPanel : Window
1273 {
1274    caption = "CornerBlocks Communication Panel";
1275    background = lightSlateGray;
1276    borderStyle = fixed;
1277    hasClose = true;
1278    tabCycle = true;
1279    clientSize = { 430, 300 };
1280 #ifdef __ANDROID__
1281    anchor = { right = 0, bottom = 0 };
1282 #endif
1283    icon = { ":ollie.png" };
1284
1285    // Other player info
1286    char playerNames[MaxPlayers][256];
1287
1288    DataField fldName { header = "Name", width = 100 };
1289    DataField fldAddr { header = "Address" };
1290
1291    bool OnClose(bool parentClosing)
1292    {
1293       if(!cornerBlocks || cornerBlocks.destroyed || cornerBlocks.Destroy(0))
1294          return true;
1295       return false;
1296    }
1297
1298    void OnDestroy()
1299    {
1300       app.Unlock();
1301       if(hosting)
1302          cornerBlocksService.Stop();
1303       app.Lock();
1304    }
1305
1306    CommunicationPanel()
1307    {
1308       listPlayers.AddField(fldName);
1309       listPlayers.AddField(fldAddr);
1310    }
1311
1312    void ListPlayers()
1313    {
1314       int c;
1315       listPlayers.Clear();
1316       fldAddr.header = hosting ? "Address" : "";
1317       for(c = 0; c<MaxPlayers; c++)
1318       {
1319          if(hosting)
1320          {
1321             if(serverPlayers[c])
1322             {
1323                DataRow row = listPlayers.AddRow();
1324                DCOMServerObject object = (DCOMServerObject)serverPlayers[c].connection._vTbl[-1];
1325
1326                row.tag = serverPlayers[c].id;
1327                row.SetData(fldName, serverPlayers[c].name);
1328                row.SetData(fldAddr, object.serverSocket.inetAddress);
1329             }
1330          }
1331          else if(playerNames[c][0])
1332          {
1333             DataRow row = listPlayers.AddRow();
1334             row.SetData(fldName, playerNames[c]);
1335          }
1336       }
1337    }
1338
1339    void UpdateControlsStates()
1340    {
1341       int numPlayers = 0;
1342       if(hosting)
1343       {
1344          int c;
1345          for(c = 0; c<MaxPlayers; c++)
1346             if(serverPlayers[c])
1347                numPlayers++;
1348       }
1349       lblServerAddress.disabled = serverAddress.disabled = cornerBlocks.server ? true : false;
1350       lblPlayerName.disabled = playerName.disabled = cornerBlocks.server ? true : false;
1351       lblServerAddress.Update(null);
1352       lblPlayerName.Update(null);
1353       btnConnect.visible = cornerBlocks.server ? false : true;
1354       btnDisconnect.visible = cornerBlocks.server ? true : false;
1355       cornerBlocks.chat.visible = cornerBlocks.server ? true : false;
1356
1357       btnHost.visible = !hosting && !cornerBlocks.server;
1358       btnStopHosting.visible = hosting;
1359       btnStart.visible = hosting && (!serverGameStarted || serverGameState.over) && numPlayers > 0;
1360       btnStopGame.visible = hosting && (serverGameStarted && !serverGameState.over);
1361       listPlayers.visible = (hosting && (serverGameStarted || numPlayers > 0)) || (!hosting && cornerBlocks.server && cornerBlocks.gameStarted);
1362       btnKick.visible = hosting && !serverGameStarted && numPlayers > 0;
1363       btnKick.disabled = listPlayers.currentRow ? false : true;
1364    }
1365    EditBox serverAddress
1366    {
1367       this, caption = "Server Address:", altA, font = { "Tahoma", 10, bold = true }, size = { 220, 24 }, position = { 16, 64 }, contents = "localhost"
1368    };
1369    Label lblServerAddress { this, font = { "Tahoma", 8.25f, bold = true }, position = { 16, 40 }, labeledWindow = serverAddress };
1370    Button btnConnect
1371    {
1372       this, caption = "Connect", altC, isDefault = true, font = { "Arial", 16, bold = true }, size = { 126, 32 }, position = { 256, 64 };
1373
1374       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1375       {
1376          if(!cornerBlocks.server)
1377          {
1378             cornerBlocks.server = ServerConnection
1379             {
1380                void OnDisconnect(int code)
1381                {
1382                   app.Lock();
1383                   if(cornerBlocks)
1384                   {
1385                      delete cornerBlocks.server;
1386                      cornerBlocks.gameStarted = false;
1387                      cornerBlocks.turnLightTimer.Stop();
1388                      cornerBlocks.lightValue = 1;
1389                      cornerBlocks.lightDir = -.1f;
1390                      cornerBlocks.Update(null);
1391                      panel.UpdateControlsStates();
1392                      panel.ListPlayers();
1393                   }
1394                   DCOMClientObject::OnDisconnect(code);
1395                   app.Unlock();
1396                }
1397
1398                void GameStarted(GameInfo gameInfo)
1399                {
1400                   int x,y;
1401                   int c, np = 0;
1402
1403                   cornerBlocks.gameState.numPlayers = gameInfo.numPlayers;
1404                   cornerBlocks.firstColor = gameInfo.firstColor;
1405                   cornerBlocks.colorPlayed = cornerBlocks.firstColor;
1406
1407                   for(c = 0; c<MaxPlayers; c++)
1408                   {
1409                      if(gameInfo.players[c][0])
1410                         strcpy(panel.playerNames[np++], gameInfo.players[c]);
1411                   }
1412
1413                   cornerBlocks.btnPass.visible = false;
1414                   cornerBlocks.gameState.NewGame();
1415                   cornerBlocks.passed[0] = false;
1416                   cornerBlocks.passed[1] = false;
1417                   cornerBlocks.passed[2] = false;
1418                   cornerBlocks.passed[3] = false;
1419                   cornerBlocks.gameStarted = true;
1420                   cornerBlocks.lightValue = 1;
1421                   cornerBlocks.lightDir = -.1f;
1422                   cornerBlocks.turnLightTimer.Start();
1423                   cornerBlocks.UpdatePlayerNames();
1424
1425                   cornerBlocks.Update(null);
1426
1427                   panel.UpdateControlsStates();
1428                   panel.ListPlayers();
1429                }
1430
1431                void GameEnded()
1432                {
1433                   int c;
1434
1435                   panel.ListPlayers();
1436                   cornerBlocks.gameStarted = false;
1437                   cornerBlocks.btnPass.visible = false;
1438                   cornerBlocks.Update(null);
1439                }
1440
1441                void MovePlayed(PlayerColor color, int pieceType, int direction, bool flip, int boardX, int boardY)
1442                {
1443                   cornerBlocks.gameState.PlayMove(pieceType, direction, flip, boardX, boardY);
1444                   if(color == cornerBlocks.colorPlayed)
1445                      cornerBlocks.NextColorPlayed();
1446 #ifdef _DEBUG
1447                   cornerBlocks.gotMove = true;
1448 #endif
1449                   if(cornerBlocks.colorPlayed == cornerBlocks.gameState.colorTurn && !cornerBlocks.gameState.over)
1450                   {
1451                      if(!cornerBlocks.gameState.validMove)
1452                      {
1453                         if(!cornerBlocks.passed[cornerBlocks.gameState.colorTurn])
1454                         {
1455                            cornerBlocks.btnPass.visible = true;
1456                            if(!cornerBlocks.active)
1457                               cornerBlocks.Flash();
1458                         }
1459                         else
1460                            cornerBlocks.server.Pass();
1461                      }
1462                      else if(!cornerBlocks.active)
1463                         cornerBlocks.Flash();
1464                   }
1465
1466                   // This hangs here, why?
1467                   /*if(cornerBlocks.gameState.over)
1468                      scoresPanel.visible = true;*/
1469                   if(cornerBlocks)
1470                   {
1471                      cornerBlocks.UpdatePlayerNames();
1472                      cornerBlocks.Update(null);
1473                   }
1474                }
1475
1476                void Passed(PlayerColor color)
1477                {
1478                   cornerBlocks.gameState.Pass();
1479                   if(color == cornerBlocks.colorPlayed)
1480                      cornerBlocks.NextColorPlayed();
1481                   else if(!cornerBlocks.active)
1482                      cornerBlocks.Flash();
1483
1484                   if(cornerBlocks.colorPlayed == cornerBlocks.gameState.colorTurn && !cornerBlocks.gameState.over)
1485                   {
1486                      if(!cornerBlocks.gameState.validMove)
1487                      {
1488                         if(!cornerBlocks.passed[cornerBlocks.gameState.colorTurn])
1489                         {
1490                            cornerBlocks.btnPass.visible = true;
1491                            if(!cornerBlocks.active)
1492                               cornerBlocks.Flash();
1493                         }
1494                         else
1495                            cornerBlocks.server.Pass();
1496                      }
1497                      else if(!cornerBlocks.active)
1498                         cornerBlocks.Flash();
1499                   }
1500
1501                   if(cornerBlocks)
1502                   {
1503                      cornerBlocks.UpdatePlayerNames();
1504                      cornerBlocks.Update(null);
1505                   }
1506                }
1507
1508                void NotifyMessage(String name, String msg)
1509                {
1510                   EditBox log = cornerBlocks.chat.log;
1511                   char * format = (log.numLines > 1 || log.line.count) ?
1512                      "\n%s: %s" : "%s: %s";
1513                   int len = strlen(msg);
1514                   // Avoid buffer overflow...
1515                   if(len >= MAX_F_STRING-100)
1516                      msg[MAX_F_STRING-100] = 0;
1517                   cornerBlocks.chat.Log(format, name, msg);
1518                }
1519             };
1520             incref cornerBlocks.server;
1521             if(cornerBlocks.server.Connect(serverAddress.contents, CORNERBLOCKS_PORT))
1522             {
1523                int playerID = cornerBlocks.server.Join();
1524                if(cornerBlocks.server && playerID != -1)
1525                   cornerBlocks.server.SetName(playerName.contents);
1526                else
1527                   cornerBlocks.server.Disconnect(0);
1528                UpdateControlsStates();
1529             }
1530             else
1531                delete cornerBlocks.server;
1532          }
1533          return true;
1534       }
1535    };
1536    Button btnDisconnect
1537    {
1538       this, caption = "Disconnect", altD, font = { "Arial", 16, bold = true }, size = { 126, 32 }, position = { 256, 64 }, visible = false;
1539
1540       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1541       {
1542          if(!cornerBlocks.gameStarted || cornerBlocks.gameState.over ||
1543             MessageBox { type = okCancel, caption = "Ecere Corner Blocks",
1544                contents = "Game in progress! Disconnect?"
1545             }.Modal() == ok)
1546          {
1547             if(cornerBlocks.server)
1548                cornerBlocks.server.Disconnect(0);
1549          }
1550          return true;
1551       }
1552    };
1553    Button btnHost
1554    {
1555       this, caption = "Host", altH, font = { "Arial", 16, bold = true }, size = { 90, 32 }, position = { 16, 112 };
1556
1557       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1558       {
1559          if(!cornerBlocks.gameStarted)
1560          {
1561             if(cornerBlocksService.Start())
1562             {
1563                hosting = true;
1564                Update(null);
1565                UpdateControlsStates();
1566             }
1567          }
1568          return true;
1569       }
1570    };
1571    Button btnStopHosting
1572    {
1573       this, caption = "Stop Hosting", altP, font = { "Arial", 16, bold = true }, position = { 16, 112 }, visible = false;
1574
1575       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1576       {
1577          int numPlayers = 0;
1578          int c;
1579          for(c = 0; c<MaxPlayers; c++)
1580             if(serverPlayers[c])
1581                numPlayers++;
1582          if(!numPlayers ||
1583             MessageBox { type = okCancel, caption = "Ecere Corner Blocks",
1584                contents = "Players connected! Stop hosting?"
1585             }.Modal() == ok)
1586          {
1587             app.Unlock();
1588             cornerBlocksService.Stop();
1589             app.Lock();
1590             hosting = false;
1591             Update(null);
1592             UpdateControlsStates();
1593          }
1594          return true;
1595       }
1596    };
1597    Button btnStart
1598    {
1599       this, caption = "Start Game", altS, font = { "Arial", 16, bold = true }, size = { 124, 32 }, position = { 256, 112 }, visible = false;
1600
1601       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1602       {
1603          if(hosting)
1604          {
1605             StartGame();
1606             UpdateControlsStates();
1607          }
1608          return true;
1609       }
1610    };
1611    Button btnStopGame
1612    {
1613       this, caption = "Stop Game", altG, font = { "Arial", 16, bold = true }, position = { 256, 112 }, visible = false;
1614
1615       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1616       {
1617          if(!serverGameStarted || serverGameState.over ||
1618             MessageBox { type = okCancel, caption = "Ecere Corner Blocks",
1619                contents = "Stop game in progress?"
1620             }.Modal() == ok)
1621          {
1622             EndGame();
1623             UpdateControlsStates();
1624          }
1625          return true;
1626       }
1627    };
1628    ListBox listPlayers
1629    {
1630       this, caption = "Players Connected", altD, size = { 236, 84 }, position = { 16, 176 }, visible = false, hasHeader = true;
1631
1632       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
1633       {
1634          UpdateControlsStates();
1635          return true;
1636       }
1637    };
1638    Label lblListPlayers { this, font = { "Tahoma", 8.25f, bold = true }, position = { 16, 152 }, visible = false, labeledWindow = listPlayers };
1639    Button btnKick
1640    {
1641       this, caption = "Kick", altK, font = { "Arial", 16, bold = true }, size = { 80, 32 }, position = { 264, 224 }, visible = false;
1642
1643       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1644       {
1645          DataRow row = listPlayers.currentRow;
1646          if(row)
1647          {
1648             int id = (int)row.tag;
1649             char msg[1024];
1650             sprintf(msg, "Kick %s?", serverPlayers[id].name);
1651             if(MessageBox { type = okCancel, caption = "Ecere Corner Blocks",
1652                   contents = msg
1653                }.Modal() == ok)
1654                KickPlayer(id);
1655          }
1656          return true;
1657       }
1658    };
1659    EditBox playerName
1660    {
1661       this, caption = "Player Name:", altN, font = { "Arial", 12 }, size = { 132, 24 }, position = { 104, 8 }, contents = "Player"
1662    };
1663    Label lblPlayerName { this, font = { "Tahoma", 8.25f, bold = true }, position = { 16, 16 }, labeledWindow = playerName };
1664 }
1665
1666 CommunicationPanel panel { };
1667
1668 DCOMService cornerBlocksService { port = CORNERBLOCKS_PORT };