1 /* This program is copyright 1993 by Andrew Plotkin.
2  The source code may be freely copied, distributed,
3  and modified, as long as this copyright notice is
4  retained.
5  */
6 
7 #include <stdio.h>
8 #include <string.h>
9 #include <X11/Xlib.h>
10 #include <X11/Xutil.h>
11 
12 #include "handwave.h"
13 #include "spelllist.h"
14 #include "patchlevel.h"
15 
16 #include "xspell.h"
17 
18 int win_wid, win_hgt;
19 
20 struct player *players;
21 int numplayers;
22 int turnstate;
23 
24 game *gameval = NULL;
25 
26 static int defaultnum = 0;
27 static char *defaultnamelist[10] = {"White", "Black", "Red", "Green", "Blue",
28 		"Yellow", "Orange", "Purple", "Grey", "Charteuse"};
29 static char *namelist[MAXPLAYERS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
30 static char *displist[MAXPLAYERS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
31 static int genderlist[MAXPLAYERS] = {Gender_MALE, Gender_MALE, Gender_MALE,
32 		Gender_MALE, Gender_MALE, Gender_MALE, Gender_MALE, Gender_MALE};
33 
34 static struct query savequery = {
35     0,
36     Qu_SaveTranscript,
37     NULL,
38     0
39 };
40 
41 static void PrintMsg(), PrintMsg2(), PrintMsg3();
42 extern void redraw();
43 extern void XQueries();
44 
main(argc,argv)45 main(argc, argv)
46 int argc;
47 char *argv[];
48 {
49     struct interface procs;
50     struct maingame ignorerock;
51     static char inbuf[256];
52     int whowon;
53     int ix, val;
54     struct player *py;
55     int movelist[MAXPLAYERS*2];
56 
57     procs.proc_PrintMsg = PrintMsg;
58     procs.proc_PrintMsg2 = PrintMsg2;
59     procs.proc_PrintMsg3 = PrintMsg3;
60     procs.proc_Queries = XQueries;
61 
62     if (argc==1) {
63 	printf("usage: spellcast RemoteDisplay [ RemoteDisplay2 ... ]\n");
64 	exit(12);
65     }
66 
67     numplayers = 1;
68     displist[0] = NULL;
69     for (ix=1; ix<argc; ix++) {
70 	if (strlen(argv[ix])!=0) {
71 	    if (index(argv[ix], ':'))
72 		displist[numplayers] = argv[ix];
73 	    else {
74 		displist[numplayers] = (char *)malloc(strlen(argv[ix])+6);
75 		strcpy(displist[numplayers], argv[ix]);
76 		strcat(displist[numplayers], ":0.0");
77 	    }
78 	}
79 	else
80 	    displist[numplayers] = NULL;
81 	/* allow an empty argument to signify NULL display */
82 	numplayers++;
83     }
84 
85     if (numplayers < 2) {
86 	printf("You have listed only %d players!\n", numplayers);
87 	exit(12);
88     }
89     if (numplayers > MAXPLAYERS) {
90 	printf("There is a maximum of %d players!\n", MAXPLAYERS);
91 	exit(12);
92     }
93 
94     players = (struct player *)malloc(sizeof(struct player) * numplayers);
95 
96     win_wid = 832;
97     if (numplayers > 3)
98 	win_wid += (numplayers-3) * (2*GEST_SIZE + 20);
99     win_hgt = 750;
100     turnstate = State_Init;
101 
102     for (ix=0; ix<numplayers; ix++) {
103 	val = win_init(ix);
104 	if (!val) {
105 	    fprintf(stderr, "spellcast: exiting.\n");
106 	    exit(-1);
107 	}
108     }
109 
110     gameval = BeginGame(numplayers, namelist, genderlist, &procs, &ignorerock);
111 
112     do {
113 	val = TurnType(gameval);
114 	turnstate = State_Top;
115 	for (ix=0; ix<numplayers; ix++) {
116 	    py = &(players[ix]);
117 	    py->turn_active = TurnPlayerActive(gameval, ix);
118 	    py->turn_done = !py->turn_active;
119 	}
120 	for (ix=0; ix<numplayers; ix++) {
121 	    py = &(players[ix]);
122 	    py->gesture_chosen[0] = Gesture_NOTHING;
123 	    py->gesture_chosen[1] = Gesture_NOTHING;
124 	    clear_answers(py);
125 	    if (py->turn_active)
126 		switch (val) {
127 		    case Turn_HASTE:
128 			add_answer(py, "Enter your gestures for your Hastened move.",
129 					"", 1, 0, NULL, 0);
130 			break;
131 		    case Turn_TIMESTOP:
132 			add_answer(py, "Enter your gestures for your Time-Stopped move.",
133 					"", 1, 0, NULL, 0);
134 			break;
135 		    default:
136 			add_answer(py,
137 "Enter your gestures for this move, at the bottom of your gesture list.",
138 					"", 1, 0, NULL, 0);
139 			break;
140 		}
141 	    else
142 		switch (val) {
143 		    case Turn_HASTE:
144 			add_answer(py, "Other people are Hastened this turn.",
145 					"", 1, 0, NULL, 0);
146 			break;
147 		    case Turn_TIMESTOP:
148 			add_answer(py, "You are frozen in time.", "", 1, 0, NULL, 0);
149 			break;
150 		    default:
151 			add_answer(py, "You are out of the game.", "", 1, 0, NULL, 0);
152 			break;
153 		}
154 	    redraw_column(py);
155 	    draw_button(py, 0, 1); /* redraw turn-end button */
156 	    redraw_queries_only(py, 0);
157 	    /*update_statlist(py);*/
158 	}
159 	mainloop();
160 
161 	for (ix=0; ix<numplayers; ix++) {
162 	    struct player *py = &(players[ix]);
163 	    movelist[ix*2+0] = py->gesture_chosen[0];
164 	    movelist[ix*2+1] = py->gesture_chosen[1];
165 	}
166 	whowon = RunTurn(gameval, movelist);
167     } while (whowon < 0);
168 
169     if (whowon==MAXPLAYERS)
170 	sprintf(inbuf, "\n+++ The game is a draw +++\n");
171     else
172 	sprintf(inbuf, "\n+++ %s has won +++\n", NameOfBeing(gameval,
173 			QuVal_Target_Wizard, whowon));
174     PrintMsg(inbuf, gameval, &ignorerock);
175     LogInTranscript(gameval, inbuf);
176     LogInTranscript(gameval, "\n");
177 
178     turnstate = State_End;
179     for (ix=0; ix<numplayers; ix++) {
180 	py = &(players[ix]);
181 	py->turn_active = 1;
182 	py->turn_done = 0;
183     }
184     for (ix=0; ix<numplayers; ix++) {
185 	py = &(players[ix]);
186 	clear_answers(py);
187 	if (ix==0)
188 	    add_answer(py, "Do you want to save a transcript of this game?",
189 			"No", 1, 0, &savequery, 0);
190 	redraw_column(py);
191 	draw_button(py, 0, 1); /* redraw turn-end button */
192 	redraw_queries_only(py, 0);
193 	/*update_statlist(py);*/
194     }
195     mainloop();
196 
197     savequery.answer = players[0].answers[0].answer;
198     if (savequery.answer) {
199 	char fname[512];
200 	FILE *fl;
201 
202 	sprintf(fname, "%s/spellcast-%d", TRANSCRIPTDIR, getpid());
203 	fl = fopen(fname, "w");
204 	if (!fl) {
205 	    printf("spellcast: unable to open %s for writing\n", fname);
206 	    perror("spellcast: error");
207 	}
208 	else {
209 	    WriteTranscript((game *)gameval, fl);
210 	    fclose(fl);
211 	    printf("Saved transcript in %s.\n", fname);
212 	}
213     }
214     FreeGame(gameval);
215 
216 }
217 
win_init(pnum)218 int win_init(pnum)
219 int pnum;
220 {
221     XSetWindowAttributes attr;
222     XGCValues gcvalues;
223     char winname[256];
224     char *fontname;
225     char *name, *tmp;
226     int ix, val;
227 
228     struct player *py = &(players[pnum]);
229 
230     py->dpy = XOpenDisplay(displist[pnum]);
231     if (!py->dpy) {
232 	fprintf(stderr, "spellcast: could not open display for player %d.\n", pnum);
233 	return 0;
234     }
235 
236     py->scn = DefaultScreen(py->dpy);
237     py->win = XCreateSimpleWindow(py->dpy, DefaultRootWindow(py->dpy), 100, 100,
238 		win_wid, win_hgt, 1, BlackPixel(py->dpy, py->scn),
239 		WhitePixel(py->dpy, py->scn));
240     py->backpm = XCreatePixmap(py->dpy, py->win, win_wid, win_hgt,
241 		DefaultDepth(py->dpy, py->scn));
242     py->backstore = 0;
243 
244     { /* make some window managers happy */
245 	XWMHints wmhints;
246 	wmhints.flags = InputHint;
247 	wmhints.input = True;
248 	XSetWMHints(py->dpy, py->win, &wmhints);
249     }
250 
251     attr.event_mask = EVENTMASK;
252     XChangeWindowAttributes(py->dpy, py->win, CWEventMask, &attr);
253 
254     tmp = XGetDefault(py->dpy, "spellcast", "name");
255     if (tmp) {
256 	name = (char *)malloc(strlen(tmp)+1);
257 	strcpy(name, tmp);
258 	tmp = index(name, ':');
259 	if (!tmp)
260 	    genderlist[pnum] = Gender_MALE;
261 	else {
262 	    switch (*(tmp+1)) {
263 		case 'f':
264 		case 'F':
265 		    genderlist[pnum] = Gender_FEMALE;
266 		    break;
267 		case 'n':
268 		case 'N':
269 		    genderlist[pnum] = Gender_NEUTER;
270 		    break;
271 		case 'x':
272 		case 'X':
273 		    genderlist[pnum] = Gender_NONE;
274 		    break;
275 		case 'm':
276 		case 'M':
277 		default:
278 		    genderlist[pnum] = Gender_MALE;
279 		    break;
280 	    }
281 	    *tmp = '\0';
282 	}
283 	tmp = name+strlen(name)-1;
284 	while (tmp>name && (*tmp==' ' || *tmp=='\t'))
285 	    tmp--;
286 	*(tmp+1) = '\0';
287 	namelist[pnum] = name;
288     }
289     else {
290 	namelist[pnum] = defaultnamelist[defaultnum];
291 	defaultnum++;
292     }
293 
294     do {
295 	val = 0;
296 	if (strlen(namelist[pnum])<1)
297 	    val = 1;
298 	if (!strcmp(namelist[pnum], "nobody"))
299 	    val = 1;
300 	if (!strcmp(namelist[pnum], "Nobody"))
301 	    val = 1;
302 	for (ix=0; ix<pnum; ix++)
303 	    if (!strcmp(namelist[pnum], namelist[ix]))
304 		val = 1;
305 
306 	if (val) {
307 	    tmp = namelist[pnum];
308 	    namelist[pnum] = defaultnamelist[defaultnum];
309 	    defaultnum++;
310 
311 	    fprintf(stderr,
312 "spellcast: the name '%s' for player %d is taken. Switching to '%s'...\n",
313 			tmp, pnum, namelist[pnum]);
314 	}
315     } while (val);
316 
317     sprintf(winname, "Spellcast: %s", namelist[pnum]);
318     XStoreName(py->dpy, py->win, winname);
319 
320     XMapWindow(py->dpy, py->win);
321 
322     fontname = XGetDefault(py->dpy, "spellcast", "font");
323     if (!fontname)
324 	fontname = BODYFONT;
325     py->font = XLoadQueryFont(py->dpy, fontname);
326 
327     if (!py->font) {
328 	fprintf(stderr,
329 "spellcast: could not find font %s for player %d. Switching to %s...\n",
330 			fontname, pnum, BODYFONT_ALT);
331 	py->font = XLoadQueryFont(py->dpy, BODYFONT_ALT);
332 	if (!py->font) {
333 	    fprintf(stderr,
334 "spellcast: could not find font %s for player %d. Switching to %s...\n",
335 			BODYFONT_ALT, pnum, BODYFONT_ALT2);
336 	    py->font = XLoadQueryFont(py->dpy, BODYFONT_ALT2);
337 	    if (!py->font) {
338 		fprintf(stderr, "spellcast: could not find font %s for player %d.\n",
339 				BODYFONT_ALT2, pnum);
340 
341 		return 0;
342 	    }
343 
344 	}
345     }
346 
347     gcvalues.font = py->font->fid;
348     gcvalues.foreground = BlackPixel(py->dpy, py->scn);
349     gcvalues.background = WhitePixel(py->dpy, py->scn);
350     py->blackgc = XCreateGC(py->dpy, py->win, GCForeground|GCBackground|GCFont,
351 		&gcvalues);
352     XSetGraphicsExposures(py->dpy, py->blackgc, 0);
353 
354     gcvalues.foreground = WhitePixel(py->dpy, py->scn);
355     gcvalues.background = BlackPixel(py->dpy, py->scn);
356     py->whitegc = XCreateGC(py->dpy, py->win, GCForeground|GCBackground|GCFont,
357 		&gcvalues);
358     XSetGraphicsExposures(py->dpy, py->whitegc, 0);
359 
360     init_bitmaps(py);
361     init_xtext(pnum);
362     init_query(py);
363     init_stats(py);
364     init_talk(py);
365     py->button_lit = (-1);
366     redraw(py);
367 
368     return 1;
369 }
370 
371 #define POPUP_ELBOW (8)
372 
adjust_rect(mrec)373 void adjust_rect(mrec)
374 struct rect *mrec;
375 {
376     if (mrec->x+mrec->w >= win_wid-POPUP_ELBOW)
377 	mrec->x = win_wid-POPUP_ELBOW - mrec->w;
378     if (mrec->y+mrec->h >= win_hgt-POPUP_ELBOW)
379 	mrec->y = win_hgt-POPUP_ELBOW - mrec->h;
380     if (mrec->x < POPUP_ELBOW)
381 	mrec->x = POPUP_ELBOW;
382     if (mrec->y < POPUP_ELBOW)
383 	mrec->y = POPUP_ELBOW;
384 }
385 
backing_store(py,mrec)386 void backing_store(py, mrec)
387 struct player *py;
388 struct rect *mrec;
389 {
390     py->backrec = (*mrec);
391     if (py->backstore) {
392 	printf("ERROR: backing store already on\n");
393     }
394     py->backstore = 1;
395     py->gotexpose = 0;
396     XCopyArea(py->dpy, py->win, py->backpm, py->blackgc, mrec->x, mrec->y,
397 		mrec->w, mrec->h, 0, 0);
398 }
399 
backing_restore(py)400 void backing_restore(py)
401 struct player *py;
402 {
403     if (!py->backstore) {
404 	printf("ERROR: backing store already off\n");
405     }
406     py->backstore = 0;
407     if (py->gotexpose) {
408 	XClearArea(py->dpy, py->win, py->backrec.x, py->backrec.y,
409 			py->backrec.w, py->backrec.h, 0);
410 	redraw(py);
411     }
412     else {
413 	XCopyArea(py->dpy, py->backpm, py->win, py->blackgc, 0, 0,
414 			py->backrec.w, py->backrec.h, py->backrec.x, py->backrec.y);
415     }
416 }
417 
PrintMsg(msg,gameval,rock)418 static void PrintMsg(msg, gameval, rock)
419 char *msg;
420 game *gameval;
421 struct maingame *rock;
422 {
423     int ix;
424     if (msg) {
425 	for (ix=0; ix<numplayers; ix++)
426 	    dump_text(ix, msg);
427     }
428 }
429 
PrintMsg2(person1,msg1,msgelse,gameval,rock)430 static void PrintMsg2(person1, msg1, msgelse, gameval, rock)
431 int person1;
432 char *msg1, *msgelse;
433 game *gameval;
434 struct maingame *rock;
435 {
436     int ix;
437 
438     for (ix=0; ix<numplayers; ix++) {
439 	if (ix==person1) {
440 	    if (msg1)
441 		dump_text(ix, msg1);
442 	}
443 	else {
444 	    if (msgelse)
445 		dump_text(ix, msgelse);
446 	}
447     }
448 }
449 
PrintMsg3(person1,person2,msg1,msg2,msgelse,gameval,rock)450 static void PrintMsg3(person1, person2, msg1, msg2, msgelse, gameval, rock)
451 int person1, person2;
452 char *msg1, *msg2, *msgelse;
453 game *gameval;
454 struct maingame *rock;
455 {
456     int ix;
457 
458     for (ix=0; ix<numplayers; ix++) {
459 	if (ix==person1) {
460 	    if (msg1)
461 		dump_text(ix, msg1);
462 	}
463 	else if (ix==person2) {
464 	    if (msg2)
465 		dump_text(ix, msg2);
466 	}
467 	else {
468 	    if (msgelse)
469 		dump_text(ix, msgelse);
470 	}
471     }
472 }
473 
474