initial interface requests
[netris.git] / game.c
1 /*
2  * Netris -- A free networked version of T*tris
3  * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id: game.c,v 1.39 1999/05/16 06:56:27 mhw Exp $
20  */
21
22 #define NOEXT
23 #include "netris.h"
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <netinet/in.h>
29
30 enum { KT_left, KT_right, KT_rotright, KT_rotleft, KT_drop, KT_down,
31         KT_toggleSpy, KT_pause, KT_faster, KT_redraw, KT_numKeys };
32
33 static char *keyNames[KT_numKeys+1] = {
34         "Left", "Right", "RotRight", "RotLeft", "Drop", "Down",
35         "ToggleSpy", "Pause", "Faster", "Redraw", NULL };
36
37 static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
38
39 static char keyTable[KT_numKeys+1];
40 static int dropModeEnable = 0;
41 static char *robotProg;
42
43 ExtFunc void MapKeys(char *newKeys)
44 {
45         int i, k, ch;
46         char used[256];
47         int errs = 0;
48
49         /* XXX assumptions about ASCII encoding here */
50         for (i = k = 0; newKeys[i] && k < KT_numKeys; i++,k++) {
51                 if (newKeys[i] == '^' && newKeys[i+1])
52                         keyTable[k] = toupper(newKeys[++i]) - ('A' - 1);
53                 else
54                         keyTable[k] = newKeys[i];
55         }
56         memset(used, 0, sizeof(used));
57         for (k = 0; k < KT_numKeys; k++) {
58                 ch = (unsigned char) keyTable[k];
59                 if (used[ch]) {
60                         if (iscntrl(ch) && ch < ' ')
61                                 sprintf(scratch, "Ctrl-%c", ch + ('A' - 1));
62                         else if (isprint(ch))
63                                 sprintf(scratch, "\"%c\"", ch);
64                         else
65                                 sprintf(scratch, "0x%X", ch);
66                         if (!errs)
67                                 fprintf(stderr, "Duplicate key mappings:\n");
68                         errs++;
69                         fprintf(stderr, "  %s mapped to both %s and %s\n",
70                                         scratch, keyNames[used[ch]-1], keyNames[k]);
71                 }
72                 used[ch] = k + 1;
73         }
74         if (errs)
75                 exit(1);
76 }
77
78 ExtFunc int StartNewPiece(int scr, Shape *shape)
79 {
80         if (nextShape[scr]) {
81                 curShape[scr] = nextShape[scr];
82                 nextShape[scr] = shape;
83         }
84         else
85                 curShape[scr] = shape;
86         curY[scr] = boardVisible[scr] + 4;
87         curX[scr] = boardWidth[scr] / 2;
88         while (!ShapeVisible(curShape[scr], scr, curY[scr], curX[scr]))
89                 --curY[scr];
90         if (!ShapeFits(curShape[scr], scr, curY[scr], curX[scr]))
91                 return 0;
92         PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1);
93         return 1;
94 }
95
96 ExtFunc void OneGame(int scr, int scr2)
97 {
98         MyEvent event;
99         int linesCleared, changed = 0;
100         int totalDrops = 0, totalLines = 0, totalAdds = 0;
101         int spied = 0, spying = 0, dropMode = 0;
102         int oldPaused = 0, paused = 0, pausedByMe = 0, pausedByThem = 0;
103         long pauseTimeLeft;
104         int pieceCount = 0;
105         int key;
106         char *p, *cmd;
107
108         speed = stepDownInterval;
109         ResetBaseTime();
110         InitBoard(scr);
111         if (scr2 >= 0) {
112                 spied = 1;
113                 spying = 1;
114                 InitBoard(scr2);
115                 UpdateOpponentDisplay();
116         }
117         ShowDisplayInfo();
118         SetITimer(speed, speed);
119         if (robotEnable) {
120                 RobotCmd(0, "GameType %s\n", gameNames[game]);
121                 RobotCmd(0, "BoardSize 0 %d %d\n",
122                                 boardVisible[scr], boardWidth[scr]);
123                 if (scr2 >= 0) {
124                         RobotCmd(0, "BoardSize 1 %d %d\n",
125                                         boardVisible[scr2], boardWidth[scr2]);
126                         RobotCmd(0, "Opponent 1 %s %s\n", opponentName, opponentHost);
127                         if (opponentFlags & SCF_usingRobot)
128                                 RobotCmd(0, "OpponentFlag 1 robot\n");
129                         if (opponentFlags & SCF_fairRobot)
130                                 RobotCmd(0, "OpponentFlag 1 fairRobot\n");
131                 }
132                 RobotCmd(0, "TickLength %.3f\n", speed / 1.0e6);
133                 RobotCmd(0, "BeginGame\n");
134                 RobotTimeStamp();
135         }
136         nextShape[scr] = ChooseOption(stdOptions);
137         while (StartNewPiece(scr, ChooseOption(stdOptions))) {
138                 ShowScore(scr, totalDrops, totalLines, totalAdds);
139                 if (robotEnable && !fairRobot)
140                         RobotCmd(1, "NewPiece %d\n", ++pieceCount);
141                 if (spied) {
142                         short shapeNum;
143                         netint2 data[1];
144
145                         shapeNum = ShapeToNetNum(curShape[scr]);
146                         data[0] = hton2(shapeNum);
147                         SendPacket(scr, NP_newPiece, sizeof(data), data);
148                 }
149                 for (;;) {
150                         changed = RefreshBoard(scr) || changed;
151                         if (spying)
152                                 changed = RefreshBoard(scr2) || changed;
153                         if (changed) {
154                                 RefreshScreen();
155                                 changed = 0;
156                         }
157                         CheckNetConn();
158                         switch (WaitMyEvent(&event, EM_any)) {
159                                 case E_alarm:
160                                         if (!MovePiece(scr, -1, 0))
161                                                 goto nextPiece;
162                                         else if (spied)
163                                                 SendPacket(scr, NP_down, 0, NULL);
164                                         break;
165                                 case E_key:
166                                         p = strchr(keyTable, tolower(event.u.key));
167                                         key = p - keyTable;
168                                         if (robotEnable) {
169                                                 RobotCmd(1, "UserKey %d %s\n",
170                                                                 (int)(unsigned char)event.u.key,
171                                                                 p ? keyNames[key] : "?");
172                                                 break;
173                                         }
174                                         if (!p)
175                                                 break;
176                                 keyEvent:
177                                         if (paused && (key != KT_pause) && (key != KT_redraw))
178                                                 break;
179                                         switch(key) {
180                                                 case KT_left:
181                                                         if (MovePiece(scr, 0, -1) && spied)
182                                                                 SendPacket(scr, NP_left, 0, NULL);
183                                                         break;
184                                                 case KT_right:
185                                                         if (MovePiece(scr, 0, 1) && spied)
186                                                                 SendPacket(scr, NP_right, 0, NULL);
187                                                         break;
188                                                 case KT_rotleft:
189                                                         if (RotatePiece(scr, 0) && spied)
190                                                                 SendPacket(scr, NP_rotleft, 0, NULL);
191                                                         break;
192                                                 case KT_rotright:
193                                                         if (RotatePiece(scr, 1) && spied)
194                                                                 SendPacket(scr, NP_rotright, 0, NULL);
195                                                         break;
196                                                 case KT_down:
197                                                         if (MovePiece(scr, -1, 0) && spied)
198                                                                 SendPacket(scr, NP_down, 0, NULL);
199                                                         SetITimer(speed, speed);
200                                                         break;
201                                                 case KT_toggleSpy:
202                                                         spying = (!spying) && (scr2 >= 0);
203                                                         break;
204                                                 case KT_drop:
205                                                         if (DropPiece(scr) > 0) {
206                                                                 if (spied)
207                                                                         SendPacket(scr, NP_drop, 0, NULL);
208                                                                 if (dropModeEnable == 2)
209                                                                         SetITimer(speed, 1); //instantdrop
210                                                                 else
211                                                                         SetITimer(speed, speed);
212                                                         }
213                                                         dropMode = dropModeEnable;
214                                                         break;
215                                                 case KT_pause:
216                                                         pausedByMe = !pausedByMe;
217                                                         if (game == GT_classicTwo) {
218                                                                 netint2 data[1];
219
220                                                                 data[0] = hton2(pausedByMe);
221                                                                 SendPacket(scr, NP_pause, sizeof(data), data);
222                                                         }
223                                                         paused = pausedByMe || pausedByThem;
224                                                         if (robotEnable)
225                                                                 RobotCmd(1, "Pause %d %d\n", pausedByMe,
226                                                                         pausedByThem);
227                                                         ShowPause(pausedByMe, pausedByThem);
228                                                         changed = 1;
229                                                         break;
230                                                 case KT_faster:
231                                                         if (game != GT_onePlayer)
232                                                                 break;
233                                                         speed = speed * 0.8;
234                                                         SetITimer(speed, SetITimer(0, 0));
235                                                         ShowDisplayInfo();
236                                                         changed = 1;
237                                                         break;
238                                                 case KT_redraw:
239                                                         ScheduleFullRedraw();
240                                                         if (paused)
241                                                                 RefreshScreen();
242                                                         break;
243                                         }
244                                         if (dropMode && DropPiece(scr) > 0) {
245                                                 if (spied)
246                                                         SendPacket(scr, NP_drop, 0, NULL);
247                                                 SetITimer(speed, speed);
248                                         }
249                                         break;
250                                 case E_robot:
251                                 {
252                                         int num;
253
254                                         cmd = event.u.robot.data;
255                                         if ((p = strchr(cmd, ' ')))
256                                                 *p++ = 0;
257                                         else
258                                                 p = cmd + strlen(cmd);
259                                         for (key = 0; keyNames[key]; ++key)
260                                                 if (!strcmp(keyNames[key], cmd) &&
261                                                                 (fairRobot || (1 == sscanf(p, "%d", &num) &&
262                                                                         num == pieceCount)))
263                                                         goto keyEvent;
264                                         if (!strcmp(cmd, "Message")) {
265                                                 Message(p);
266                                                 changed = 1;
267                                         }
268                                         break;
269                                 }
270                                 case E_net:
271                                         switch(event.u.net.type) {
272                                                 case NP_giveJunk:
273                                                 {
274                                                         netint2 data[2];
275                                                         short column;
276
277                                                         memcpy(data, event.u.net.data, sizeof(data[0]));
278                                                         column = Random(0, boardWidth[scr]);
279                                                         data[1] = hton2(column);
280                                                         InsertJunk(scr, ntoh2(data[0]), column);
281                                                         if (spied)
282                                                                 SendPacket(scr, NP_insertJunk, sizeof(data), data);
283 //                                                      Message("Opponent added %d lines");
284                                                         break;
285                                                 }
286                                                 case NP_newPiece:
287                                                 {
288                                                         short shapeNum;
289                                                         netint2 data[1];
290
291                                                         FreezePiece(scr2);
292                                                         memcpy(data, event.u.net.data, sizeof(data));
293                                                         shapeNum = ntoh2(data[0]);
294                                                         StartNewPiece(scr2, NetNumToShape(shapeNum));
295                                                         break;
296                                                 }
297                                                 case NP_down:
298                                                         MovePiece(scr2, -1, 0);
299                                                         break;
300                                                 case NP_left:
301                                                         MovePiece(scr2, 0, -1);
302                                                         break;
303                                                 case NP_right:
304                                                         MovePiece(scr2, 0, 1);
305                                                         break;
306                                                 case NP_rotleft:
307                                                         RotatePiece(scr2, 0);
308                                                         break;
309                                                 case NP_rotright:
310                                                         RotatePiece(scr2, 1);
311                                                         break;
312                                                 case NP_drop:
313                                                         DropPiece(scr2);
314                                                         break;
315                                                 case NP_clear:
316                                                         ClearFullLines(scr2);
317                                                         break;
318                                                 case NP_insertJunk:
319                                                 {
320                                                         netint2 data[2];
321
322                                                         memcpy(data, event.u.net.data, sizeof(data));
323                                                         InsertJunk(scr2, ntoh2(data[0]), ntoh2(data[1]));
324                                                         break;
325                                                 }
326                                                 case NP_pause:
327                                                 {
328                                                         netint2 data[1];
329
330                                                         memcpy(data, event.u.net.data, sizeof(data));
331                                                         pausedByThem = ntoh2(data[0]);
332                                                         paused = pausedByMe || pausedByThem;
333                                                         if (robotEnable)
334                                                                 RobotCmd(1, "Pause %d %d\n", pausedByMe,
335                                                                         pausedByThem);
336                                                         ShowPause(pausedByMe, pausedByThem);
337                                                         changed = 1;
338                                                         break;
339                                                 }
340                                                 default:
341                                                         break;
342                                         }
343                                         break;
344                                 case E_lostRobot:
345                                 case E_lostConn:
346                                         goto gameOver;
347                                 default:
348                                         break;
349                         }
350                         if (paused != oldPaused) {
351                                 if (paused)
352                                         pauseTimeLeft = SetITimer(0, 0);
353                                 else
354                                         SetITimer(speed, pauseTimeLeft);
355                                 oldPaused = paused;
356                         }
357                 }
358         nextPiece:
359                 dropMode = 0;
360                 FreezePiece(scr);
361                 totalDrops++;
362                 if ((linesCleared = ClearFullLines(scr)) > 0) {
363                         totalLines += linesCleared;
364                         totalAdds += linesCleared - (linesCleared < 4);
365                 }
366                 if (linesCleared > 0 && spied)
367                         SendPacket(scr, NP_clear, 0, NULL);
368                 if (game == GT_classicTwo && linesCleared > 1) {
369                         short junkLines;
370                         netint2 data[1];
371
372                         junkLines = linesCleared - (linesCleared < 4);
373                         data[0] = hton2(junkLines);
374                         SendPacket(scr, NP_giveJunk, sizeof(data), data);
375                 }
376         }
377 gameOver:
378         SetITimer(0, 0);
379 }
380
381 ExtFunc int main(int argc, char **argv)
382 {
383         int initConn = 0, waitConn = 0, ch;
384         char *hostStr = NULL, *portStr = NULL;
385
386         standoutEnable = colorEnable = 1;
387         stepDownInterval = DEFAULT_INTERVAL;
388         MapKeys(DEFAULT_KEYS);
389         while ((ch = getopt(argc, argv, "hHRs:r:Fk:c:wodDSCp:i:")) != -1)
390                 switch (ch) {
391                         case 'c':
392                                 initConn = 1;
393                                 hostStr = optarg;
394                                 break;
395                         case 'w':
396                                 waitConn = 1;
397                                 break;
398                         case 'p':
399                                 portStr = optarg;
400                                 break;
401                         case 'i':
402                                 stepDownInterval = atof(optarg) * 1e6;
403                                 break;
404                         case 's':
405                                 initSeed = atoi(optarg);
406                                 myFlags |= SCF_setSeed;
407                                 break;
408                         case 'r':
409                                 robotEnable = 1;
410                                 robotProg = optarg;
411                                 myFlags |= SCF_usingRobot;
412                                 break;
413                         case 'F':
414                                 fairRobot = 1;
415                                 myFlags |= SCF_fairRobot;
416                                 break;
417                         case 'd':
418                                 dropModeEnable = 1;
419                                 break;
420                         case 'D':
421                                 dropModeEnable = 2;
422                                 break;
423                         case 'C':
424                                 colorEnable = 0;
425                                 break;
426                         case 'S':
427                                 standoutEnable = 0;
428                                 break;
429                         case 'k':
430                                 MapKeys(optarg);
431                                 break;
432                         case 'H':
433                                 DistInfo();
434                                 exit(0);
435                         case 'R':
436                                 Rules();
437                                 exit(0);
438                         case 'h':
439                                 Usage();
440                                 exit(0);
441                         default:
442                                 Usage();
443                                 exit(1);
444                 }
445         if (optind < argc || (initConn && waitConn)) {
446                 Usage();
447                 exit(1);
448         }
449         if (fairRobot && !robotEnable)
450                 fatal("You can't use the -F option without the -r option");
451         InitUtil();
452         if (robotEnable)
453                 InitRobot(robotProg);
454         InitNet();
455         InitScreens();
456         if (initConn || waitConn) {
457                 MyEvent event;
458
459                 game = GT_classicTwo;
460                 if (initConn)
461                         InitiateConnection(hostStr, portStr);
462                 else if (waitConn)
463                         WaitForConnection(portStr);
464                 {
465                         netint4 data[2];
466                         int major;
467
468                         data[0] = hton4(MAJOR_VERSION);
469                         data[1] = hton4(PROTOCOL_VERSION);
470                         SendPacket(0, NP_version, sizeof(data), data);
471                         if (WaitMyEvent(&event, EM_net) != E_net)
472                                 fatal("Network negotiation failed");
473                         memcpy(data, event.u.net.data, sizeof(data));
474                         major = ntoh4(data[0]);
475                         protocolVersion = ntoh4(data[1]);
476                         if (event.u.net.type != NP_version || major < MAJOR_VERSION)
477                                 fatal("Your opponent is using an old, incompatible version\n"
478                                         "of Netris.  They should get the latest version.");
479                         if (major > MAJOR_VERSION)
480                                 fatal("Your opponent is using an newer, incompatible version\n"
481                                         "of Netris.  Get the latest version.");
482                         if (protocolVersion > PROTOCOL_VERSION)
483                                 protocolVersion = PROTOCOL_VERSION;
484                 }
485                 if (protocolVersion < 3 && stepDownInterval != DEFAULT_INTERVAL)
486                         fatal("Your opponent's version of Netris predates the -i option.\n"
487                                         "For fairness, you shouldn't use the -i option either.");
488                 {
489                         netint4 data[3];
490                         int len;
491                         int seed;
492
493                         if (protocolVersion >= 3)
494                                 len = sizeof(data);
495                         else
496                                 len = sizeof(netint4[2]);
497                         if ((myFlags & SCF_setSeed))
498                                 seed = initSeed;
499                         else
500                                 seed = time(0);
501                         if (waitConn)
502                                 SRandom(seed);
503                         data[0] = hton4(myFlags);
504                         data[1] = hton4(seed);
505                         data[2] = hton4(stepDownInterval);
506                         SendPacket(0, NP_startConn, len, data);
507                         if (WaitMyEvent(&event, EM_net) != E_net ||
508                                         event.u.net.type != NP_startConn)
509                                 fatal("Network negotiation failed");
510                         memcpy(data, event.u.net.data, len);
511                         opponentFlags = ntoh4(data[0]);
512                         seed = ntoh4(data[1]);
513                         if (initConn) {
514                                 if ((opponentFlags & SCF_setSeed) != (myFlags & SCF_setSeed))
515                                         fatal("If one player sets the random number seed, "
516                                                         "both must.");
517                                 if ((myFlags & SCF_setSeed) && seed != initSeed)
518                                         fatal("Both players have set the random number seed, "
519                                                         "and they are unequal.");
520                                 if (protocolVersion >= 3 && stepDownInterval != ntoh4(data[2]))
521                                         fatal("Your opponent is using a different step-down "
522                                                 "interval (-i).\nYou must both use the same one.");
523                                 SRandom(seed);
524                         }
525                 }
526                 {
527                         char *userName;
528                         int len, i;
529
530                         userName = getenv("LOGNAME");
531                         if (!userName || !userName[0])
532                                 userName = getenv("USER");
533                         if (!userName || !userName[0])
534                                 strcpy(userName, "???");
535                         len = strlen(userName)+1;
536                         if (len > sizeof(opponentName))
537                                 len = sizeof(opponentName);
538                         SendPacket(0, NP_userName, len, userName);
539                         if (WaitMyEvent(&event, EM_net) != E_net ||
540                                         event.u.net.type != NP_userName)
541                                 fatal("Network negotiation failed");
542                         strncpy(opponentName, event.u.net.data,
543                                 sizeof(opponentName)-1);
544                         opponentName[sizeof(opponentName)-1] = 0;
545                         for (i = 0; opponentName[i]; ++i)
546                                 if (!isprint(opponentName[i]))
547                                         opponentName[i] = '?';
548                         for (i = 0; opponentHost[i]; ++i)
549                                 if (!isprint(opponentHost[i]))
550                                         opponentHost[i] = '?';
551                 }
552                 OneGame(0, 1);
553         }
554         else {
555                 game = GT_onePlayer;
556                 OneGame(0, -1);
557         }
558         return 0;
559 }
560
561 /*
562  * vi: ts=4 ai
563  * vim: noai si
564  */