1 /*
2  *   XFrisk - The classic board game for X
3  *   Copyright (C) 1993-1999 Elan Feingold (elan@aetherworks.com)
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *   $Id: utils.c,v 1.6 1999/11/13 21:58:32 morphy Exp $
20  */
21 
22 #include <X11/X.h>
23 #include <X11/Intrinsic.h>
24 #include <X11/cursorfont.h>
25 #include <X11/StringDefs.h>
26 #include <X11/Shell.h>
27 
28 #include <X11/Xaw/AsciiText.h>
29 #include <X11/Xaw/List.h>
30 #include <X11/Xaw/Form.h>
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 
37 #include "utils.h"
38 #include "network.h"
39 #include "riskgame.h"
40 #include "gui-vars.h"
41 #include "client.h"
42 #include "colormap.h"
43 #include "callbacks.h"
44 #include "game.h"
45 #include "dice.h"
46 #include "debug.h"
47 
48 #define MAX_LINES 5
49 
50 #define MAX_DIGITS 3
51 #define MAX_STRING "888"
52 
53 /* Globals */
54 static Int32    iQueryResult;
55 static Cursor   cursorWait=0, cursorPlay=0;
56 
57 /* Private functions */
58 void UTIL_SetCountryBrightness(Int32 iCountry, Boolean fLight);
59 
60 /************************************************************************
61  *  FUNCTION: UTIL_PrintArmies
62  *  HISTORY:
63  *     01.28.94  ESF  Created.
64  *     02.05.94  ESF  Added number centering (finally!)
65  *     03.30.94  ESF  Changed EXTRA_Y to be smaller.
66  *     04.02.94  ESF  Fixed font problem, not XSetFont'ing.
67  *     05.05.94  ESF  Fixed so that 0 armies doesn't print anything.
68  *  PURPOSE:
69  *  NOTES:
70  ************************************************************************/
UTIL_PrintArmies(Int32 iCountry,Int32 iNumArmies,Int32 iColor)71 void UTIL_PrintArmies(Int32 iCountry, Int32 iNumArmies, Int32 iColor)
72 {
73   Int32   x, y, iWidth, iHeight;
74   Char    strNumber[4];
75 
76   /* Is number of armies right? */
77   if (iNumArmies>999)
78     {
79 #ifdef ENGLISH
80       (void)UTIL_PopupDialog("Warning", "UTIL: Too many armies",
81 			     1, "Ok", NULL, NULL);
82 #endif
83 #ifdef FRENCH
84       (void)UTIL_PopupDialog("Attention", "UTIL: Trop d'arm�es",
85 			     1, "Oui", NULL, NULL);
86 #endif
87       return;
88     }
89 
90   /* Setup what we want to print (could be empty) */
91   if (iNumArmies>0)
92     snprintf(strNumber, sizeof(strNumber), "%d", iNumArmies);
93   else
94     snprintf(strNumber, sizeof(strNumber), " ");
95 
96   /* Erase the old number, assume three digits */
97   x       = RISK_GetTextXOfCountry(iCountry);
98   y       = RISK_GetTextYOfCountry(iCountry);
99   iHeight = (pFont->max_bounds.ascent + pFont->max_bounds.descent);
100   iWidth  = XTextWidth(pFont, MAX_STRING, MAX_DIGITS);
101 
102   /* Draw erasing rectangle on screen and pixmap */
103   XSetForeground(hDisplay, hGC, COLOR_QueryColor(COLOR_CountryToColor(iCountry)));
104   XFillRectangle(hDisplay, hWindow, hGC, x, y-iHeight,
105 		 iWidth, iHeight+1);
106   XFillRectangle(hDisplay, pixMapImage, hGC, x, y-iHeight,
107 		 iWidth, iHeight+1);
108 
109   /* Center the number, based on the number of digits */
110   if (strlen(strNumber)==1)
111     x += iWidth/3;
112   else if (strlen(strNumber)==2)
113     x += iWidth/4;
114 
115   /* Finally draw the text in color requested. */
116   XSetForeground(hDisplay, hGC, iColor);
117   XSetFont(hDisplay, hGC, pFont->fid);
118 
119   if (iNumArmies>=0)
120     {
121       XDrawString(hDisplay, hWindow, hGC, x, y, strNumber,
122 		  strlen(strNumber));
123       XDrawString(hDisplay, pixMapImage, hGC, x, y, strNumber,
124 		  strlen(strNumber));
125     }
126 
127   /* Flush the queue of requests */
128   XFlush(hDisplay);
129 }
130 
131 
132 /************************************************************************
133  *  FUNCTION: UTIL_DisplayMessage
134  *  HISTORY:
135  *     02.04.94  ESF  Created.
136  *     01.15.95  ESF  Fixed memory leak.
137  *     04.30.95  ESF  Got rid of newlines in incoming message.
138  *     04.30.95  ESF  Improved multiline message handling.
139  *     29.08.95  JC   Put the newline in the beginning of the message.
140  *     29.08.95  JC   strFrom -> iFrom, added iTo.
141  *  PURPOSE:
142  *  NOTES:
143  ************************************************************************/
UTIL_DisplayMessage(Int32 iFrom,Int32 iTo,CString strMessage)144 void UTIL_DisplayMessage(Int32 iFrom, Int32 iTo, CString strMessage)
145 {
146   static Flag fFirst = TRUE;
147   Int32         i, iCount;
148   CString       strOldMessage, strNewMessage;
149   const char *strFrom = NULL, *strNewLine = "", *strTo = "";
150   int has_a_to = 1;
151   size_t len;
152 
153 #ifdef ENGLISH
154   const char *strToHead = "To ";
155 #endif
156 #ifdef FRENCH
157   const char *strToHead = "� ";
158 #endif
159 
160   /* Get the old text */
161   XtVaGetValues(wMsgText, XtNstring, &strOldMessage, NULL);
162 
163   /* Remove any newlines from new message */
164   for (i=0, iCount=strlen(strMessage); i!=iCount; i++)
165     if (strMessage[i]=='\n')
166       strMessage[i]=' ';
167 
168   /* Build up the new message */
169   if (fFirst)
170       fFirst = FALSE;
171   else
172       strNewLine = "\n";
173 
174   switch (iFrom)
175     {
176     case FROM_SERVER:
177 #ifdef ENGLISH
178       strFrom = "Server";
179 #endif
180 #ifdef FRENCH
181       strFrom = "Serveur";
182 #endif
183       break;
184     case FROM_UNKNOW:
185 #ifdef ENGLISH
186       strFrom = "Unknown";
187 #endif
188 #ifdef FRENCH
189       strFrom = "Inconnu";
190 #endif
191       break;
192     default:
193       D_Assert(iFrom >= -1 && iFrom < MAX_PLAYERS, "Bogus sender!");
194       strFrom = RISK_GetNameOfPlayer(iFrom);
195     }
196 
197   switch (iTo)
198     {
199     case DST_ALLPLAYERS:
200 #ifdef ENGLISH
201       strTo = "Everybody";
202 #endif
203 #ifdef FRENCH
204       strTo = "tous";
205 #endif
206       break;
207     case DST_OTHER:
208       has_a_to = 0;
209       break;
210     default:
211       strTo = RISK_GetNameOfPlayer(iTo);
212     }
213 
214   len = strlen(strOldMessage) + strlen(strNewLine) + strlen(strFrom) + 2
215 	+ 1 + strlen(strToHead) + strlen(strTo) + 4 + strlen(strMessage) + 1;
216   strNewMessage = (CString)MEM_Alloc(len);
217 
218   snprintf(strNewMessage, len,
219 	   "%s%s%s: %s%s%s%s%s%s", strOldMessage, strNewLine, strFrom,
220 	   has_a_to ? "("       : "",
221 	   has_a_to ? strToHead : "",
222 	   has_a_to ? strTo     : "",
223 	   has_a_to ? ") \""    : "",
224 	   strMessage,
225 	   has_a_to ? "\""      : "");
226 
227   /* Set the new message, go to the end */
228   XtVaSetValues(wMsgText, XtNstring, strNewMessage, NULL);
229   XawTextSetInsertionPoint(wMsgText, strlen(strNewMessage));
230 
231   MEM_Free(strNewMessage);
232 }
233 
234 
235 /************************************************************************
236  *  FUNCTION: UTIL_DisplayComment
237  *  HISTORY:
238  *     02.07.94  ESF  Created.
239  *  PURPOSE:
240  *  NOTES:
241  ************************************************************************/
UTIL_DisplayComment(CString strComment)242 void UTIL_DisplayComment(CString strComment)
243 {
244   Arg     pArgs[1];
245   Int32   iCount;
246 
247   iCount = 0;
248   XtSetArg(pArgs[iCount], XtNlabel, strComment); iCount++;
249   XtSetValues(wCommentLabel, pArgs, iCount);
250 }
251 
252 
253 
254 /************************************************************************
255  *  FUNCTION: UTIL_SetPlayerTurnIndicator
256  *  HISTORY:
257  *     03.03.94  ESF  Created.
258  *  PURPOSE:
259  *  NOTES:
260  ************************************************************************/
UTIL_SetPlayerTurnIndicator(Int32 iPlayer)261 void UTIL_SetPlayerTurnIndicator(Int32 iPlayer)
262 {
263   UNUSED(iPlayer);
264 
265   D_Assert(FALSE, "Unimplemented!");
266 }
267 
268 
269 /************************************************************************
270  *  FUNCTION: UTIL_DisplayActionCString
271  *  HISTORY:
272  *     03.03.94  ESF  Created.
273  *     03.29.94  ESF  Added player attack mode history.
274  *     03.30.94  ESF  Fixed bug, not erasing iAttackSrc.
275  *     04.02.94  ESF  Added clearing of error.
276  *     05.07.94  ESF  Removed clearing of error.
277  *  PURPOSE:
278  *  NOTES:
279  ************************************************************************/
UTIL_DisplayActionCString(Int32 iState,Int32 iPlayer)280 void UTIL_DisplayActionCString(Int32 iState, Int32 iPlayer)
281 {
282   char buf[256];
283   switch (iState)
284     {
285     case STATE_REGISTER:
286 #ifdef ENGLISH
287       snprintf(buf, sizeof(buf), "Waiting for all clients to register...");
288 #endif
289 #ifdef FRENCH
290       snprintf(buf, sizeof(buf), "Attent que tous les clients aient fini de s'enregistrer...");
291 #endif
292       break;
293 
294     case STATE_FORTIFY:
295 #ifdef ENGLISH
296       snprintf(buf, sizeof(buf), "%s to place an army (%d remaining)...",
297 #endif
298 #ifdef FRENCH
299       snprintf(buf, sizeof(buf), "%s place une arm�e (reste %d)...",
300 #endif
301 	      RISK_GetNameOfPlayer(iPlayer),
302 	      RISK_GetNumArmiesOfPlayer(iPlayer));
303       XawListHighlight(wActionList, iActionState = ACTION_PLACE);
304       if (UTIL_PlayerIsLocal(iPlayer))
305         {
306           XawListHighlight(wAttackList,
307 		           RISK_GetDiceModeOfPlayer(iCurrentPlayer));
308           XawListHighlight(wMsgDestList,
309 		           RISK_GetMsgDstModeOfPlayer(iCurrentPlayer));
310         }
311       break;
312 
313 
314     case STATE_PLACE:
315 #ifdef ENGLISH
316       snprintf(buf, sizeof(buf), "%s placing armies (%d remaining)...",
317 #endif
318 #ifdef FRENCH
319       snprintf(buf, sizeof(buf), "%s place des arm�es (reste %d)...",
320 #endif
321 	      RISK_GetNameOfPlayer(iPlayer),
322 	      RISK_GetNumArmiesOfPlayer(iPlayer));
323       XawListHighlight(wActionList, iActionState = ACTION_PLACE);
324       if (UTIL_PlayerIsLocal(iPlayer))
325         {
326           XawListHighlight(wAttackList,
327 		           RISK_GetDiceModeOfPlayer(iCurrentPlayer));
328           XawListHighlight(wMsgDestList,
329 		           RISK_GetMsgDstModeOfPlayer(iCurrentPlayer));
330         }
331       break;
332 
333     case STATE_ATTACK:
334 #ifdef ENGLISH
335       snprintf(buf, sizeof(buf), "%s attacking...",
336 #endif
337 #ifdef FRENCH
338       snprintf(buf, sizeof(buf), "%s attaque...",
339 #endif
340 	      RISK_GetNameOfPlayer(iPlayer));
341       iActionState = RISK_GetAttackModeOfPlayer(iCurrentPlayer);
342       XawListHighlight(wActionList,
343 		       RISK_GetAttackModeOfPlayer(iCurrentPlayer));
344       if (UTIL_PlayerIsLocal(iPlayer))
345         {
346           XawListHighlight(wAttackList,
347 		           RISK_GetDiceModeOfPlayer(iCurrentPlayer));
348           XawListHighlight(wMsgDestList,
349 		           RISK_GetMsgDstModeOfPlayer(iCurrentPlayer));
350         }
351       break;
352 
353     case STATE_MOVE:
354       if (GAME_AttackFrom() >= 0)
355 	{
356 	  UTIL_DarkenCountry(GAME_AttackFrom());
357 	  GAME_SetAttackSrc(-1);
358 	}
359 
360 #ifdef ENGLISH
361       snprintf(buf, sizeof(buf), "%s executing a free move...",
362 #endif
363 #ifdef FRENCH
364       snprintf(buf, sizeof(buf), "%s d�place...",
365 #endif
366 	      RISK_GetNameOfPlayer(iPlayer));
367       XawListHighlight(wActionList, iActionState = ACTION_MOVE);
368       if (UTIL_PlayerIsLocal(iPlayer))
369         {
370           XawListHighlight(wAttackList,
371 		           RISK_GetDiceModeOfPlayer(iCurrentPlayer));
372           XawListHighlight(wMsgDestList,
373 		           RISK_GetMsgDstModeOfPlayer(iCurrentPlayer));
374         }
375       break;
376 
377     default:
378       D_Assert(FALSE, "Shouldn't be here!");
379     }
380 
381   UTIL_DisplayComment(buf);
382 }
383 
384 
385 /************************************************************************
386  *  FUNCTION: UTIL_DisplayError
387  *  HISTORY:
388  *     03.04.94  ESF  Created.
389  *  PURPOSE:
390  *  NOTES:
391  ************************************************************************/
392 void UTIL_DisplayError(CString strError)
393 {
394   Arg     pArgs[1];
395   Int32   iCount;
396 
397   iCount=0;
398   XtSetArg(pArgs[iCount], XtNlabel, strError); iCount++;
399   XtSetValues(wErrorLabel, pArgs, iCount);
400 }
401 
402 
403 /************************************************************************
404  *  FUNCTION: UTIL_ServerEnterState
405  *  HISTORY:
406  *     03.05.94  ESF  Created.
407  *     11.16.94  ESF  Added to send messages to other clients.
408  *  PURPOSE:
409  *  NOTES:
410  ************************************************************************/
411 void UTIL_ServerEnterState(Int32 iNewState)
412 {
413   MsgEnterState   m;
414   MsgNetMessage   msgNetMessage;
415   char buf[256];
416 
417   switch (iNewState)
418     {
419     case STATE_REGISTER:
420       break;
421     case STATE_PLACE:
422 #ifdef ENGLISH
423       snprintf(buf, sizeof(buf), "%s is placing %s.",
424 	      RISK_GetNameOfPlayer(iCurrentPlayer),
425 	      (RISK_GetNumArmiesOfPlayer(iCurrentPlayer) > 1 ?
426 	       "armies" :
427 	       "an army"));
428 #endif
429 #ifdef FRENCH
430       snprintf(buf, sizeof(buf), "%s place %s arm�e%s.",
431 	      RISK_GetNameOfPlayer(iCurrentPlayer),
432 	      (RISK_GetNumArmiesOfPlayer(iCurrentPlayer)>1)?"des":"une",
433 	      (RISK_GetNumArmiesOfPlayer(iCurrentPlayer)>1)?"s":"");
434 #endif
435       break;
436     case STATE_FORTIFY:
437 #ifdef ENGLISH
438       snprintf(buf, sizeof(buf), "%s is fortifying territories.",
439 #endif
440 #ifdef FRENCH
441       snprintf(buf, sizeof(buf), "%s fortifie des territoires.",
442 #endif
443 	      RISK_GetNameOfPlayer(iCurrentPlayer));
444       break;
445     case STATE_ATTACK:
446 #ifdef ENGLISH
447       snprintf(buf, sizeof(buf), "%s is attacking.",
448 #endif
449 #ifdef FRENCH
450       snprintf(buf, sizeof(buf), "%s attaque.",
451 #endif
452 	      RISK_GetNameOfPlayer(iCurrentPlayer));
453       break;
454     case STATE_MOVE:
455 #ifdef ENGLISH
456       snprintf(buf, sizeof(buf), "%s is moving armie(s).",
457 #endif
458 #ifdef FRENCH
459       snprintf(buf, sizeof(buf), "%s d�place des arm�e(s).",
460 #endif
461 	      RISK_GetNameOfPlayer(iCurrentPlayer));
462       break;
463     default:
464       D_Assert(0, "Bogus state!");
465     }
466 
467   msgNetMessage.strMessage = buf;
468   (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
469 			 MSG_NETMESSAGE, &msgNetMessage);
470 
471   /* Change states and let the server know about it */
472   m.iState = iNewState;
473   (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
474 			 MSG_ENTERSTATE, &m);
475 }
476 
477 
478 /* Utility for the next function */
479 #define UTIL_CloseArmyDialog() \
480  UTIL_DisplayError(""); \
481  XtSetKeyboardFocus(wToplevel, wToplevel); \
482  XtRemoveGrab(wArmiesShell); \
483  XtUnrealizeWidget(wArmiesShell);
484 
485 /************************************************************************
486  *  FUNCTION: UTIL_GetArmyNumber
487  *  HISTORY:
488  *     03.16.94  ESF  Created.
489  *     03.28.94  ESF  Fixed minor printing bug.
490  *     04.01.94  ESF  Fixed bug in centering shell, had XtNy with an x.
491  *  PURPOSE:
492  *  NOTES:
493  ************************************************************************/
494 Int32 UTIL_GetArmyNumber(Int32 iMinArmies, Int32 iMaxArmies, Flag fLetCancel)
495 {
496   Int32        x, y, iNumArmies;
497   XEvent       xEvent;
498   CString      strBuffer;
499   char buf[256];
500 
501   /* Make sure that the range is valid */
502   D_Assert(iMinArmies<=iMaxArmies, "Invalid range for army values!");
503 
504   UTIL_CenterShell(wArmiesShell, wToplevel, &x, &y);
505   XtVaSetValues(wArmiesShell,
506 		XtNallowShellResize, False,
507 		XtNx, x,
508 		XtNy, y,
509 		XtNborderWidth, 1,
510 		XtNtitle, "Frisk",
511 		NULL);
512 
513   /* Set the default number of armies as the maximum */
514   snprintf(buf, sizeof(buf), "%d", iMaxArmies);
515   XtVaSetValues(wArmiesText, XtNstring, buf, NULL);
516 
517   /* Don't display the cancel button if there is no chance of this */
518   XtVaSetValues(wCancelArmiesButton, XtNsensitive,
519 		fLetCancel ? True : False, NULL);
520 
521   /* Pop it up */
522   XtMapWidget(wArmiesShell);
523   XtAddGrab(wArmiesShell, True, True);
524   XtSetKeyboardFocus(wToplevel, wArmiesShell);
525   XtSetKeyboardFocus(wArmiesShell, wArmiesText);
526 
527  keep_going:
528   iQueryResult = QUERY_INPROGRESS;
529   while (iQueryResult == QUERY_INPROGRESS)
530     {
531       /* pass events */
532       XNextEvent(hDisplay, &xEvent);
533       XtDispatchEvent(&xEvent);
534     }
535 
536   /* User must have selected one of the buttons */
537   if (!fLetCancel && iQueryResult == QUERY_NO)
538     goto keep_going;
539   else if (iQueryResult == QUERY_NO)
540     {
541       UTIL_CloseArmyDialog();
542       return(0);
543     }
544 
545   /* Get number of armies */
546   XtVaGetValues(wArmiesText, XtNstring, &strBuffer, NULL);
547   iNumArmies = atoi(strBuffer);
548 
549   if (iNumArmies<iMinArmies || iNumArmies>iMaxArmies)
550     {
551       if (iNumArmies < iMinArmies)
552 #ifdef ENGLISH
553 	snprintf(buf, sizeof(buf), "You must move at least %d armie%s.",
554 #endif
555 #ifdef FRENCH
556 	snprintf(buf, sizeof(buf), "Il faut d�placer au moins %d arm�e%s.",
557 #endif
558                 iMinArmies, (iMinArmies>1)?"s":"");
559       else
560 #ifdef ENGLISH
561 	snprintf(buf, sizeof(buf), "You can't move more than %d armie%s.",
562 #endif
563 #ifdef FRENCH
564 	snprintf(buf, sizeof(buf), "Impossible de d�placer plus de %d arm�e%s.",
565 #endif
566                 iMaxArmies, (iMaxArmies>1)?"s":"");
567       UTIL_DisplayError(buf);
568 
569       goto keep_going;
570     }
571 
572   UTIL_CloseArmyDialog();
573   return(iNumArmies);
574 }
575 
576 
577 void UTIL_QueryYes(Widget w, XtPointer pData, XtPointer pCalldata) {
578   UNUSED(w);
579   UNUSED(pData);
580   UNUSED(pCalldata);
581 
582   iQueryResult = QUERY_YES;
583 }
584 
585 void UTIL_QueryNo(Widget w, XtPointer pData, XtPointer pCalldata) {
586   UNUSED(w);
587   UNUSED(pData);
588   UNUSED(pCalldata);
589 
590   iQueryResult = QUERY_NO;
591 }
592 
593 void UTIL_QueryCancel(Widget w, XtPointer pData, XtPointer pCalldata) {
594   UNUSED(w);
595   UNUSED(pData);
596   UNUSED(pCalldata);
597 
598   iQueryResult = QUERY_CANCEL;
599 }
600 
601 
602 /************************************************************************
603  *  FUNCTION: UTIL_CenterShell
604  *  HISTORY:
605  *     03.16.94  ESF  Created.
606  *  PURPOSE:
607  *  NOTES:
608  ************************************************************************/
609 void UTIL_CenterShell(Widget wCenter, Widget wBase, Int32 *x, Int32 *y)
610 {
611   Dimension  dimWidth, dimHeight, dimTopWidth, dimTopHeight;
612   Window     hDummyWindow;
613 
614   /* Make sure the two widgets are realized */
615   if (!XtIsRealized(wCenter))
616     XtRealizeWidget(wCenter);
617   if (!XtIsRealized(wBase))
618     XtRealizeWidget(wBase);
619 
620   /* Get the dimensions of the shells and center the popup. */
621   XtVaGetValues(wCenter,
622 		XtNwidth, &dimWidth,
623 		XtNheight, &dimHeight, NULL);
624   XtVaGetValues(wBase,
625 		XtNwidth, &dimTopWidth,
626 		XtNheight, &dimTopHeight, NULL);
627 
628   XTranslateCoordinates(hDisplay, XtWindow(wBase),
629 			XDefaultRootWindow(hDisplay),
630 			(Int32)(dimTopWidth-dimWidth)/2,
631 			(Int32)(dimTopHeight-dimHeight)/2,
632 			x, y, &hDummyWindow);
633 }
634 
635 
636 /************************************************************************
637  *  FUNCTION: UTIL_LightCountry
638  *  HISTORY:
639  *     03.28.94  ESF  Created.
640  *  PURPOSE:
641  *  NOTES:
642  ************************************************************************/
643 void UTIL_LightCountry(Int32 iCountry)
644 {
645   UTIL_PrintArmies(iCountry,
646 		   RISK_GetNumArmiesOfCountry(iCountry),
647 		   WhitePixel(hDisplay, 0));
648 }
649 
650 
651 /************************************************************************
652  *  FUNCTION: UTIL_DarkenCountry
653  *  HISTORY:
654  *     03.28.94  ESF  Created.
655  *  PURPOSE:
656  *  NOTES:
657  ************************************************************************/
658 void UTIL_DarkenCountry(Int32 iCountry)
659 {
660   UTIL_PrintArmies(iCountry,
661 		   RISK_GetNumArmiesOfCountry(iCountry),
662 		   BlackPixel(hDisplay, 0));
663 }
664 
665 
666 /************************************************************************
667  *  FUNCTION: UTIL_PopupDialog
668  *  HISTORY:
669  *     04.02.94  ESF  Created.
670  *     05.12.94  ESF  Revamped.
671  *     10.30.94  ESF  Added sanity check for hDisplay.
672  *     01.25.95  ESF  Added code to center buttons.
673  *     01.25.95  ESF  Changed the default title to "Frisk".
674  *     04.30.95  ESF  Fixed a bug with resizing of the label.
675  *  PURPOSE:
676  *  NOTES:
677  ************************************************************************/
678 Int32 UTIL_PopupDialog(CString strTitle, CString strLabel, Int32 iNumOptions,
679 		       CString strOption1, CString strOption2,
680 		       CString strOption3)
681 {
682   CString               pstrOptions[3];
683   Int32                 i, x, y, iWidth;
684   XEvent                xEvent;
685   static XFontStruct   *pLabelFont=NULL;
686 
687   /* If this got called before the display is set up */
688   if (!hDisplay)
689     {
690       printf("%s: %s\n", strTitle, strLabel);
691       return 0;
692     }
693 
694   /* Initialize these for later */
695   pstrOptions[0] = strOption1;
696   pstrOptions[1] = strOption2;
697   pstrOptions[2] = strOption3;
698 
699   /* Get the font if necessary */
700   if (pLabelFont==NULL)
701     if ((pLabelFont=XLoadQueryFont(hDisplay, "*helvetica-b*-o-*14*"))==NULL)
702       {
703 	printf("Can't find the font \"*helvetica-b*-o-*14*\"!!\n");
704 	UTIL_ExitProgram(-1);
705       }
706 
707   /* Sanity check */
708   if (iNumOptions>3 || iNumOptions<1)
709     {
710       (void)UTIL_PopupDialog("Warning", "UTIL: Wacked # of options",
711 			     1, "Ok", NULL, NULL);
712       return(-1);
713     }
714 
715   /* Make sure the dialog is realized */
716   if(!XtIsRealized(wDialogShell))
717      XtRealizeWidget(wDialogShell);
718 
719   /* Set the title */
720   XtVaSetValues(wDialogShell,
721 		XtNtitle, strTitle == NULL ? "Frisk" : strTitle,
722 		XtNallowShellResize, True,
723 		NULL);
724 
725   /* Make sure that the label is big enough to hold the string, and
726    * if the length of the button strings are larger, make it bigger.
727    * Let there be a minimum width of 200...  There are a few magic
728    * numbers here, excuse them :)
729    */
730 
731   iWidth = MAX(200,
732 	       MAX(XTextWidth(pLabelFont, strLabel, strlen(strLabel))+50,
733 		   XTextWidth(pLabelFont, strOption1,
734 			      strOption1 ? strlen(strOption1) : 0)+
735 		   XTextWidth(pLabelFont, strOption2,
736 			      strOption2 ? strlen(strOption2) : 0)+
737 		   XTextWidth(pLabelFont, strOption3,
738 			      strOption3 ? strlen(strOption3) : 0)+
739 		   140));
740 
741   /* Set the button resources */
742   for (i=0; i!=3; i++)
743     if (iNumOptions>i)
744       {
745 	XtMapWidget(wDialogButton[i]);
746 	XtVaSetValues(wDialogButton[i],
747 		      XtNlabel, pstrOptions[i]==NULL ? "Null": pstrOptions[i],
748 		      XtNfromHoriz, i>0 ? wDialogButton[i-1] : NULL,
749 		      NULL);
750       }
751     else
752       {
753 	XtUnmapWidget(wDialogButton[i]);
754 	XtVaSetValues(wDialogButton[i],
755 		      XtNfromHoriz, 0,
756 		      NULL);
757       }
758 
759   /* We need to unrealize it so that the changes will take place (?) */
760   XtUnrealizeWidget(wDialogShell);
761 
762   /* Center and resize the shell, and then actually pop the dialog up */
763   XtVaSetValues(wDialogShell, XtNwidth, iWidth, NULL);
764   XtVaSetValues(wDialogLabel,
765 		XtNlabel, strLabel,
766 		XtNwidth, iWidth,
767 		NULL);
768 
769   UTIL_CenterShell(wDialogShell, wToplevel, &x, &y);
770   XtVaSetValues(wDialogShell,
771 		XtNx, x,
772 		XtNy, y,
773 		XtNborderWidth, 1,
774 		NULL);
775 
776   XtPopup(wDialogShell, XtGrabExclusive);
777 
778   iQueryResult = QUERY_INPROGRESS;
779   while (iQueryResult == QUERY_INPROGRESS)
780     {
781       /* pass events */
782       XNextEvent(hDisplay, &xEvent);
783       XtDispatchEvent(&xEvent);
784     }
785 
786   /* User must have selected one of the buttons */
787   XtPopdown(wDialogShell);
788   return(iQueryResult);
789 }
790 
791 
792 /************************************************************************
793  *  FUNCTION: UTIL_PlayerIsLocal
794  *  HISTORY:
795  *     04.11.94  ESF  Created.
796  *  PURPOSE:
797  *  NOTES:
798  ************************************************************************/
799 Flag UTIL_PlayerIsLocal(Int32 iPlayer)
800 {
801   return (RISK_GetClientOfPlayer(iPlayer) == CLNT_GetThisClientID() ? TRUE : FALSE);
802 }
803 
804 
805 /************************************************************************
806  *  FUNCTION: UTIL_NumPlayersAtClient
807  *  HISTORY:
808  *     05.04.94  ESF  Created.
809  *     05.13.95  ESF  Fixed bug, wasn't checking if player was allocated.
810  *  PURPOSE:
811  *  NOTES:
812  ************************************************************************/
813 Int32 UTIL_NumPlayersAtClient(Int32 iClient)
814 {
815   Int32 i, iCount;
816 
817   /* It's an error to call this with an out of range number */
818   D_Assert(iClient>=0 && iClient<MAX_CLIENTS, "Client is bogus!");
819 
820   for (i=iCount=0; i!=MAX_PLAYERS; i++)
821     if (RISK_GetAllocationStateOfPlayer(i) == ALLOC_COMPLETE &&
822 	RISK_GetClientOfPlayer(i) == iClient)
823       iCount++;
824 
825   return iCount;
826 }
827 
828 
829 /************************************************************************
830  *  FUNCTION:
831  *  HISTORY:
832  *     05.06.94  ESF  Created.
833  *  PURPOSE:
834  *  NOTES:
835  ************************************************************************/
836 void UTIL_RefreshMsgDest(Int32 iNumCStrings)
837 {
838   /* Update the listbox */
839   XtVaSetValues(wMsgDestList,
840 		XtNlist, pstrMsgDstCString,
841 		XtNnumberStrings, iNumCStrings,
842 		NULL);
843 }
844 
845 
846 /************************************************************************
847  *  FUNCTION: UTIL_ExitProgram
848  *  HISTORY:
849  *     06.16.94  ESF  Created.
850  *     08.03.94  ESF  Fixed to send server message before exiting.
851  *     08.28.94  ESF  Changed to close sockets.
852  *     10.02.94  ESF  Changed to not close sockets.
853  *     25.08.95  JC   Changed to not send server message if closed.
854  *  PURPOSE:
855  *  NOTES:
856  ************************************************************************/
857 void UTIL_ExitProgram(Int32 iExitValue)
858 {
859   if (CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()) != -1)
860       (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
861 			     MSG_DEREGISTERCLIENT, NULL);
862   MEM_TheEnd();
863   exit(iExitValue);
864 }
865 
866 
867 /************************************************************************
868  *  FUNCTION: UTIL_SetCursorShape
869  *  HISTORY:
870  *     10.01.94  ESF  Created.
871  *  PURPOSE:
872  *  NOTES:
873  ************************************************************************/
874 void UTIL_SetCursorShape(Int32 iShape)
875 {
876   /* If the cursors haven't been created then create them */
877   if (cursorPlay==0)
878     cursorPlay = XCreateFontCursor(hDisplay, XC_crosshair);
879   if (cursorWait==0)
880     cursorWait = XCreateFontCursor(hDisplay, XC_watch);
881 
882   /* Switch to the desired cursor */
883   switch (iShape)
884     {
885     case CURSOR_WAIT:
886       XDefineCursor(hDisplay, hWindow, cursorWait);
887       break;
888 
889     case CURSOR_PLAY:
890       XDefineCursor(hDisplay, hWindow, cursorPlay);
891       break;
892 
893     default:
894       D_Assert(FALSE, "Bogus cursor being passed in.");
895     }
896 }
897 
898 
899 /************************************************************************
900  *  FUNCTION: UTIL_PlaceNotification
901  *  HISTORY:
902  *     10.30.94  ESF  Created.
903  *     11.06.94  ESF  Completed.
904  *     01.15.95  ESF  Fixed memory leak.
905  *  PURPOSE:
906  *  NOTES:
907  ************************************************************************/
908 void UTIL_PlaceNotification(XtPointer msgMess, XtIntervalId *pId)
909 {
910   MsgPlaceNotify  *msg = (MsgPlaceNotify *)msgMess;
911   UTIL_SetCountryBrightness(msg->iCountry, pId == NULL ? TRUE : FALSE);
912 
913   /* If it's the second time we are being called then there will
914    * be a pId and we can free the memory taken by it.
915    */
916 
917   if (pId != NULL)
918     MEM_Free(msg);
919 }
920 
921 
922 /************************************************************************
923  *  FUNCTION: UTIL_AttackNotification
924  *  HISTORY:
925  *     10.30.94  ESF  Created.
926  *     11.16.94  ESF  Finished.
927  *     01.15.95  ESF  Fixed memory leak.
928  *  PURPOSE:
929  *  NOTES:
930  ************************************************************************/
931 void UTIL_AttackNotification(XtPointer msgMess, XtIntervalId *pId)
932 {
933   MsgAttackNotify  *msg = (MsgAttackNotify *)msgMess;
934   UTIL_SetCountryBrightness(msg->iSrcCountry, pId == NULL ? TRUE : FALSE);
935   UTIL_DrawNiceLine(msg->iSrcCountry, msg->iDstCountry);
936 
937   /* If it's the second time we are being called then there will
938    * be a pId and we can free the memory taken by it.
939    */
940 
941   if (pId != NULL)
942     MEM_Free(msg);
943 }
944 
945 
946 /************************************************************************
947  *  FUNCTION: UTIL_MoveNotification
948  *  HISTORY:
949  *     10.30.94  ESF  Created.
950  *     11.16.94  ESF  Finished.
951  *     01.15.95  ESF  Fixed memory leak.
952  *  PURPOSE:
953  *  NOTES:
954  ************************************************************************/
955 void UTIL_MoveNotification(XtPointer msgMess, XtIntervalId *pId)
956 {
957   MsgMoveNotify  *msg = (MsgMoveNotify *)msgMess;
958   UTIL_SetCountryBrightness(msg->iSrcCountry, pId == NULL ? TRUE : FALSE);
959   UTIL_DrawNiceLine(msg->iSrcCountry, msg->iDstCountry);
960 
961   /* If it's the second time we are being called then there will
962    * be a pId and we can free the memory taken by it.
963    */
964 
965   if (pId != NULL)
966     MEM_Free(msg);
967 }
968 
969 #define BORDER 2
970 
971 /************************************************************************
972  *  FUNCTION: UTIL_DrawNiceLine
973  *  HISTORY:
974  *     11.06.94  ESF  Created.
975  *     11.16.94  ESF  Finished.
976  *  PURPOSE:
977  *  NOTES:
978  ************************************************************************/
979 void UTIL_DrawNiceLine(Int32 iSrcCountry, Int32 iDstCountry)
980 {
981   Int32  x1, y1, x2, y2, iHalfHeight, iHalfWidth;
982   Int32  iWidth, iHeight, ixDist, iyDist;
983 
984   /* Get the two endpoints of the line */
985   x1 = RISK_GetTextXOfCountry(iSrcCountry)-BORDER;
986   y1 = RISK_GetTextYOfCountry(iSrcCountry)+BORDER;
987   x2 = RISK_GetTextXOfCountry(iDstCountry)-BORDER;
988   y2 = RISK_GetTextYOfCountry(iDstCountry)+BORDER;
989 
990   /* Get the width and height of the box containing the text. */
991   iHeight = (pFont->max_bounds.ascent + pFont->max_bounds.descent)+2*BORDER;
992   iWidth  = XTextWidth(pFont, MAX_STRING, MAX_DIGITS)+2*BORDER;
993 
994   /* Depending on where the source and destination of the line is,
995    * draw it coming and going from the middle of one of the sides,
996    * of from the corners.  Thus, the line can come and go from eight
997    * different points, which should be enough to make it look good.
998    */
999 
1000   ixDist = (x1-x2<0) ? (x2-x1) : (x1-x2);
1001   iyDist = (y1-y2<0) ? (y2-y1) : (y1-y2);
1002 
1003   /* The compiler should do this (CSE), but I'm distrustful by nature */
1004   iHalfHeight = iHeight / 2;
1005   iHalfWidth  = iWidth / 2;
1006 
1007   if (ixDist > iyDist)
1008     {
1009       /* Use the middles of the sides */
1010       if (x2 > x1)
1011 	x1+=iWidth, y1-=iHalfHeight, y2-=iHalfHeight;
1012       else
1013 	x2+=iWidth, y1-=iHalfHeight, y2-=iHalfHeight;
1014     }
1015   else
1016     {
1017       /* Use the middles of the top and bottom */
1018       if (y2 > y1)
1019 	x1+=iHalfWidth, x2+=iHalfWidth, y2-=iHeight;
1020       else
1021 	x1+=iHalfWidth, x2+=iHalfWidth, y1-=iHeight;
1022     }
1023 
1024   if (!COLOR_IsTrueColor())
1025     XDrawLine(hDisplay, hWindow, hGC_XOR, x1, y1, x2, y2);
1026   XFlush(hDisplay);
1027 }
1028 
1029 
1030 /************************************************************************
1031  *  FUNCTION: UTIL_SetCountryBrightness
1032  *  HISTORY:
1033  *     11.16.94  ESF  Created.
1034  *  PURPOSE:
1035  *  NOTES:
1036  ************************************************************************/
1037 void UTIL_SetCountryBrightness(Int32 iCountry, Boolean fLight)
1038 {
1039   Int32 iLightRefCount;
1040 
1041   iLightRefCount = CLNT_GetLightCountOfCountry(iCountry);
1042 
1043   /* Light up or darken country, using reference counting. */
1044   if (fLight)
1045     {
1046       if (iLightRefCount == 0)
1047 	UTIL_LightCountry(iCountry);
1048       CLNT_SetLightCountOfCountry(iCountry, iLightRefCount + 1);
1049     }
1050   else
1051     {
1052       if (iLightRefCount == 1)
1053 	UTIL_DarkenCountry(iCountry);
1054       CLNT_SetLightCountOfCountry(iCountry, iLightRefCount - 1);
1055     }
1056 }
1057 
1058 
1059 /************************************************************************
1060  *  FUNCTION: UTIL_OpenFile
1061  *  HISTORY:
1062  *     12.31.94  ESF  Created.
1063  *  PURPOSE:
1064  *  NOTES:
1065  ************************************************************************/
1066 FILE *UTIL_OpenFile(CString strName, CString strOptions)
1067 {
1068   FILE     *hFile;
1069   CString   strNewName = NULL;
1070   Int32     i;
1071 
1072   /* First just try to open the complete file that was passed */
1073   if ((hFile = fopen(strName, strOptions)) != NULL)
1074     return hFile;
1075 
1076   /* Construct an alternate name that is the same file
1077    * but in the current directory.  Try to open this.
1078    */
1079 
1080   /* Alloc some memory, with room for '\0' and './' */
1081   strNewName = (CString)MEM_Alloc(strlen(strName)+1+2);
1082 
1083   /* Find the last occurance of '/' in the filename */
1084   for (i=strlen(strName)-1; i>=0 && strName[i]!='/'; i--)
1085     ; /* TwiddleThumbs() */
1086 
1087   /* Create the start of the new pathname */
1088   strcpy(strNewName, "./");
1089 
1090   /* Move the pointer along to the last '/', but don't run off the end */
1091   strName = strName + MIN(i+1, (Int32)strlen(strName));
1092   strcat(strNewName, strName);
1093 
1094   /* Try to open this file */
1095   hFile = fopen(strName, strOptions);
1096 
1097   /* Free the memory */
1098   MEM_Free(strNewName);
1099 
1100   /* Return the handle */
1101   return hFile;
1102 }
1103