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: callbacks.c,v 1.17 2000/01/17 21:53:06 tony Exp $
20 *
21 * Used by client/AI
22 * Notes:
23 * should not use X, so ai can be compiled on X-less box
24 * is included by riskgame.c, maybe fix there?
25 * or this be the X part, and move out other messages?
26 */
27
28 #include <X11/X.h>
29 #include <X11/Intrinsic.h>
30 #include <X11/StringDefs.h>
31 #include <X11/Shell.h>
32
33 #include <X11/Xaw/List.h>
34 #include <X11/Xaw/Toggle.h>
35
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <unistd.h>
40
41 #include "language.h"
42 #include "network.h"
43 #include "gui-vars.h"
44 #include "gui-func.h"
45 #include "utils.h"
46 #include "callbacks.h"
47 #include "version.h"
48 #include "riskgame.h"
49 #include "client.h"
50 #include "dice.h"
51 #include "cards.h"
52 #include "game.h"
53 #include "colormap.h"
54 #include "help.h"
55 #include "colorEdit.h"
56 #include "registerPlayers.h"
57 #include "debug.h"
58 #include "viewStats.h"
59
60
61 /* Game globals -- move elsewhere? -- make part of riskgame object state! */
62 static Flag fPlayingRemotely=FALSE;
63 static Flag fForceExchange=FALSE;
64
65 static Int32 iCountryToFortify=-1;
66 static Int32 piMsgDstPlayerID[MAX_PLAYERS+1];
67 static Flag fWaitMission = FALSE;
68
69 static Flag fHaveSeenFirstPlayer;
70
71 Int32 iIndexMD;
72
73 Int32 iActionState=ACTION_PLACE;
74 Int32 iReply;
75 CString pstrMsgDstCString[MAX_PLAYERS+1];
76 Int32 iFirstPlayer;
77 Int32 iState, iCurrentPlayer;
78 Flag fGameStarted=FALSE;
79
80 /************************************************************************
81 * FUNCTION: CBK_XIncomingMessage
82 * HISTORY:
83 * 03.17.94 ESF Created.
84 * 06.24.94 ESF Fixed memory leak bug.
85 * PURPOSE:
86 * NOTES:
87 ************************************************************************/
CBK_XIncomingMessage(XtPointer pClientData,int * iSource,XtInputId * id)88 void CBK_XIncomingMessage(XtPointer pClientData, int *iSource, XtInputId *id)
89 {
90 Int32 iMessType;
91 void *pvMess;
92
93 UNUSED(pClientData);
94 UNUSED(id);
95
96 (void)RISK_ReceiveMessage(*iSource, &iMessType, &pvMess);
97 CBK_IncomingMessage(iMessType, pvMess);
98 NET_DeleteMessage(iMessType, pvMess);
99 }
100
101
102 /************************************************************************
103 * FUNCTION: CBK_IncomingMessage
104 * HISTORY:
105 * 01.26.94 ESF Created.
106 * 01.27.94 ESF Coded MSG_MESSAGEPACKET case.
107 * 01.29.94 ESF Changed MSG_MESSAGEPACKET to scroll correctly.
108 * 02.05.94 ESF Added MSG_REGISTERPLAYER handling.
109 * 03.02.94 ESF Added more army placing glue.
110 * 03.04.94 ESF Factored out code, cleaned up.
111 * 03.05.94 ESF Added color-coded player turn indicator call.
112 * 03.06.94 ESF Fixed initialization of iCurrentPlayer bug.
113 * 03.16.94 ESF Added continent bonuses.
114 * 03.17.94 ESF Factored out X code so that this could be more general.
115 * 03.17.94 ESF Fixed bug, player indicator broken for remote case.
116 * 03.17.94 ESF Added fPlayingRemotely setting.
117 * 03.18.94 ESF Completed MSG_UPDATEARMIES code.
118 * 03.28.94 ESF Added code for MSG_ENDOFGAME and MSG_DEADPLAYER.
119 * 03.29.94 ESF Added code for MSG_CARDPACKET.
120 * 03.29.94 ESF Fixed bug in handling on MSG_UPDATEARMIES.
121 * 03.29.94 ESF Added handling for MSG_REPLYPACKET.
122 * 04.11.94 ESF Added handling for MSG_CARDPACKET.
123 * 05.12.94 ESF Added handling for MSG_DELETEMSGDST.
124 * 05.17.94 ESF Added handling for MSG_NETMESSAGE.
125 * 05.19.94 ESF Added verbose message when exit occurs.
126 * 08.28.94 ESF Added handling for MSG_POPUPREGISTERBOX.
127 * 09.01.94 ESF Added handling for MSG_NETPOPUP.
128 * 10.29.94 ESF Added handling for MSG_DICEROLL.
129 * 10.29.94 ESF Added handling for MSG_MOVENOTIFY.
130 * 10.29.94 ESF Added handling for MSG_ATTACKNOTIFY.
131 * 10.29.94 ESF Added handling for MSG_PLACEROLL.
132 * 01.15.95 ESF Removed handling for MSG_DELETEMSGDST.
133 * 24.08.95 JC Added handling for MSG_MISSION.
134 * 25.08.95 JC new game if MSG_ENDOFGAME and winner is -1.
135 * 25.08.95 JC Start the game only after a fortification.
136 * 28.08.95 JC Added handling for MSG_ENDOFMISSION, MSG_VICTORY.
137 * Now MSG_ENDOFGAME => new game
138 * PURPOSE:
139 * NOTES:
140 ************************************************************************/
CBK_IncomingMessage(Int32 iMessType,void * pvMess)141 void CBK_IncomingMessage(Int32 iMessType, void *pvMess)
142 {
143 switch(iMessType)
144 {
145 case MSG_NOMESSAGE:
146 /* NoOp */
147 break;
148
149 case MSG_NETMESSAGE:
150 UTIL_DisplayComment(((MsgNetMessage *)(pvMess))->strMessage);
151 break;
152
153 case MSG_PLACENOTIFY:
154 {
155 /* Copy the message, since we are using it for a while */
156 MsgPlaceNotify *msgMess =
157 (MsgPlaceNotify *)MEM_Alloc(sizeof(MsgPlaceNotify));
158 memcpy((void *)msgMess, (void *)pvMess, sizeof(MsgPlaceNotify));
159
160 /* Do the notification, and then add a timeout for erasing it */
161 UTIL_PlaceNotification((XtPointer)msgMess, NULL);
162 XtAppAddTimeOut(appContext, NOTIFY_TIME,
163 UTIL_PlaceNotification, msgMess);
164 }
165 break;
166
167 case MSG_MOVENOTIFY:
168 {
169 /* Copy the message, since we are using it for a while */
170 MsgMoveNotify *msgMess =
171 (MsgMoveNotify *)MEM_Alloc(sizeof(MsgMoveNotify));
172 memcpy((void *)msgMess, (void *)pvMess, sizeof(MsgMoveNotify));
173
174 /* Do the notification, and then add a timeout for erasing it */
175 UTIL_MoveNotification((XtPointer)msgMess, NULL);
176 XtAppAddTimeOut(appContext, NOTIFY_TIME,
177 UTIL_MoveNotification, msgMess);
178 }
179 break;
180
181 case MSG_ATTACKNOTIFY:
182 {
183 /* Copy the message, since we are using it for a while */
184 MsgAttackNotify *msgMess =
185 (MsgAttackNotify *)MEM_Alloc(sizeof(MsgAttackNotify));
186 memcpy((void *)msgMess, (void *)pvMess, sizeof(MsgAttackNotify));
187
188 /* Do the notification, and then add a timeout for erasing it */
189 UTIL_AttackNotification((XtPointer)msgMess, NULL);
190 XtAppAddTimeOut(appContext, NOTIFY_TIME,
191 UTIL_AttackNotification, msgMess);
192 }
193 break;
194
195 case MSG_DICEROLL:
196 {
197 MsgDiceRoll *pmsgDiceRoll = (MsgDiceRoll *)pvMess;
198
199 /* Set the dice colors to be the correct ones */
200 COLOR_CopyColor(COLOR_PlayerToColor(iCurrentPlayer),
201 COLOR_DieToColor(0));
202 COLOR_CopyColor(COLOR_PlayerToColor(pmsgDiceRoll->iDefendingPlayer),
203 COLOR_DieToColor(1));
204
205 /* Erase what was there, and then display the dice */
206 DICE_Hide();
207 DICE_DrawDice(pmsgDiceRoll->pAttackDice, pmsgDiceRoll->pDefendDice);
208 }
209 break;
210
211 case MSG_NETPOPUP:
212 {
213 #ifdef ENGLISH
214 (void)UTIL_PopupDialog("Message from Server",
215 ((MsgNetPopup *)pvMess)->strMessage, 1,
216 "Ok", NULL, NULL);
217 #endif
218 #ifdef FRENCH
219 (void)UTIL_PopupDialog("Message du Serveur",
220 ((MsgNetPopup *)pvMess)->strMessage, 1,
221 "Oui", NULL, NULL);
222 #endif
223 }
224 break;
225
226 case MSG_POPUPREGISTERBOX:
227 {
228 /* Clear any text... */
229 UTIL_DisplayError("");
230 UTIL_DisplayComment("");
231
232 REG_PopupDialog();
233 }
234 break;
235
236 case MSG_FORCEEXCHANGECARDS:
237 {
238 if (iState != STATE_REGISTER)
239 {
240 if (((MsgForceExchangeCards *)pvMess)->iPlayer >= 0)
241 {
242 #ifdef ENGLISH
243 UTIL_DisplayComment("You have too many cards"
244 " and must exchange a set.");
245 #endif
246 #ifdef FRENCH
247 UTIL_DisplayComment("Vous avez trop de cartes"
248 " et devez �changer");
249 #endif
250 fForceExchange = fCanExchange = TRUE;
251 CBK_ShowCards((Widget)NULL, (XtPointer)NULL, (XtPointer)NULL);
252
253 iState = STATE_PLACE;
254 UTIL_ServerEnterState(iState);
255 }
256 }
257 }
258 break;
259
260 case MSG_REPLYPACKET:
261 iReply = ((MsgReplyPacket *)pvMess)->iReply;
262 break;
263
264 case MSG_ENDOFMISSION:
265 {
266 MsgEndOfMission *pmsgEndOfMission = (MsgEndOfMission *)pvMess;
267 Int32 iWinner = pmsgEndOfMission->iWinner;
268
269 D_Assert(iWinner >= 0 && iWinner < MAX_PLAYERS, "Bogus Winner!");
270
271 /* Print out a final message */
272 GAME_MissionAccomplied(iWinner, pmsgEndOfMission->iTyp,
273 pmsgEndOfMission->iNum1,
274 pmsgEndOfMission->iNum2);
275 }
276 break;
277
278 case MSG_VICTORY:
279 {
280 Int32 iWinner = ((MsgVictory *)pvMess)->iWinner;
281
282 D_Assert(iWinner >= 0 && iWinner < MAX_PLAYERS, "Bogus Winner!");
283
284 /* Print out a final message */
285 GAME_Victory(iWinner);
286 }
287 break;
288
289 case MSG_ENDOFGAME:
290 {
291 /* Set the cursor so that the client doesn't have
292 * the icon of the hourglass by any chance, because
293 * it doesn't really make sense.
294 */
295
296 UTIL_SetCursorShape(CURSOR_PLAY);
297
298 /* Print out a final message */
299 fHaveSeenFirstPlayer = FALSE;
300 GAME_GameOverMan();
301 }
302 break;
303
304 case MSG_MESSAGEPACKET:
305 {
306 MsgMessagePacket *pMess = (MsgMessagePacket *)pvMess;
307 UTIL_DisplayMessage(pMess->iFrom, pMess->iTo, pMess->strMessage);
308 }
309 break;
310
311 case MSG_TURNNOTIFY:
312 {
313 #ifdef ASSERTIONS
314 Int32 i;
315 #endif
316 MsgTurnNotify *pTurn = (MsgTurnNotify *)pvMess;
317
318 iCurrentPlayer = pTurn->iPlayer;
319 if(!fHaveSeenFirstPlayer) {
320 fHaveSeenFirstPlayer = TRUE;
321 iFirstPlayer = iCurrentPlayer;
322 }
323
324 /* Set the color of the player color-coded indicator */
325 GUI_SetColorOfCurrentPlayer(COLOR_PlayerToColor(iCurrentPlayer));
326
327 if (pTurn->iClient == CLNT_GetThisClientID())
328 {
329 fPlayingRemotely = FALSE;
330
331 /* Set the cursor to indicate play */
332 UTIL_SetCursorShape(CURSOR_PLAY);
333
334 /* See if everyone has finished fortifying their territories.
335 * If they have not, then there is a serious problem.
336 */
337
338 if (!fGameStarted && (iState == STATE_FORTIFY) &&
339 (RISK_GetNumArmiesOfPlayer(iCurrentPlayer) == 0))
340 {
341 fGameStarted = TRUE;
342 iCountryToFortify = -1;
343
344 #ifdef ASSERTIONS
345 /* Sanity check */
346 for (i=0;
347 i!=RISK_GetNumLivePlayers();
348 i++)
349 {
350 Int32 iPlayer = RISK_GetNthLivePlayer(i);
351 Int32 iFirstAttacker;
352 Int32 j;
353
354 D_Assert(RISK_GetNumArmiesOfPlayer(iPlayer)>=0,
355 "Bogus number of armies.");
356
357 D_Assert(RISK_GetNumArmiesOfPlayer(iPlayer) == 0,
358 "This player has armies and shouldn't!");
359
360 /* I not entirely sure this correct. But it's at least a
361 * little better than before. People who have already had
362 * their first turn are allowed to have cards. --Pac */
363 for(j=0;j!=RISK_GetNumLivePlayers();++j)
364 if(RISK_GetNthLivePlayer(j)==iFirstPlayer)
365 break;
366 D_Assert(j!=RISK_GetNumLivePlayers(),
367 "first player isn't alive?");
368 /* negative % positive = negative, unfortunately */
369 iFirstAttacker=j-NUM_COUNTRIES%RISK_GetNumLivePlayers();
370 while(iFirstAttacker<0)
371 iFirstAttacker+=RISK_GetNumLivePlayers();
372 iFirstAttacker=RISK_GetNthLivePlayer(iFirstAttacker);
373 D_Assert(iPlayer < iCurrentPlayer
374 || iPlayer >= iFirstAttacker
375 || RISK_GetNumCardsOfPlayer(iPlayer) == 0,
376 "This player has cards and shouldn't!");
377 }
378 #endif
379 }
380
381 if (fGameStarted)
382 {
383 /* Force the player to exchange if he or she possesses
384 * 5 cards or more.
385 */
386
387 if (RISK_GetNumCardsOfPlayer(iCurrentPlayer) >= 5)
388 {
389 #ifdef ENGLISH
390 UTIL_DisplayComment("You have more than five cards"
391 " and must exchange a set.");
392 #endif
393 #ifdef FRENCH
394 UTIL_DisplayComment("Vous avez plus de cinq cartes"
395 " et devez �changer");
396 #endif
397 fForceExchange = TRUE;
398 CBK_ShowCards((Widget)NULL,
399 (XtPointer)NULL, (XtPointer)NULL);
400 }
401
402 /* Go into placing state, give the player as many armies as
403 * he or she deserves by dividing the total number of
404 * countries owned divided by 3, for a minimum of three
405 * armies.
406 */
407
408 iState = STATE_PLACE;
409 UTIL_ServerEnterState(iState);
410
411 /* All of the armies should have been used up */
412 D_Assert(RISK_GetNumArmiesOfPlayer(iCurrentPlayer) == 0,
413 "Number of armies should be 0!");
414
415 GAME_SetTurnArmiesOfPlayer(iCurrentPlayer);
416 }
417 else if (iState == STATE_FORTIFY)
418 {
419 UTIL_ServerEnterState(iState);
420 if (iCountryToFortify != -1)
421 {
422 GAME_PlaceClick(iCountryToFortify, PLACE_ONE);
423 iCountryToFortify = -1;
424 UTIL_DisplayError("");
425 }
426 }
427 UTIL_DisplayActionCString(iState, iCurrentPlayer);
428 }
429 else
430 {
431 char buf[256];
432 fPlayingRemotely = TRUE;
433
434 /* Set the cursor to indicate waiting */
435 UTIL_SetCursorShape(CURSOR_WAIT);
436
437 #ifdef ENGLISH
438 snprintf(buf, sizeof(buf), "%s is playing remotely...",
439 #endif
440 #ifdef FRENCH
441 snprintf(buf, sizeof(buf), "%s joue � distance...",
442 #endif
443 RISK_GetNameOfPlayer(iCurrentPlayer));
444 UTIL_DisplayComment(buf);
445 }
446 }
447 break;
448
449 case MSG_EXIT:
450 /* Popup telling what happened */
451 #ifdef ENGLISH
452 (void)UTIL_PopupDialog("Exit Notification",
453 "Terminating game! Goodbye...",
454 1, "Ok", NULL, NULL);
455 #endif
456 #ifdef FRENCH
457 (void)UTIL_PopupDialog("Notification d'abandon",
458 "Jeu termin�! Au revoir...",
459 1, "Ok", NULL, NULL);
460 #endif
461 close(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()));
462 CLNT_SetCommLinkOfClient(CLNT_GetThisClientID(), -1);
463 UTIL_ExitProgram(0);
464 break;
465
466 case MSG_MISSION:
467 {
468 Int32 iPlayer;
469
470 if (!fWaitMission)
471 {
472 if (UTIL_PlayerIsLocal(iCurrentPlayer))
473 iPlayer = iCurrentPlayer;
474 else if (UTIL_NumPlayersAtClient(CLNT_GetThisClientID()) == 1)
475 iPlayer = RISK_GetNthPlayerAtClient(CLNT_GetThisClientID(), 0);
476 else
477 iPlayer = -1;
478 if (iPlayer != -1)
479 {
480 GAME_ShowMission(iPlayer);
481 #ifdef ENGLISH
482 (void)UTIL_PopupDialog("Mission Notification",
483 "The mission is visible in the "
484 "message zone",
485 1, "Ok", NULL, NULL);
486 #endif
487 #ifdef FRENCH
488 (void)UTIL_PopupDialog("Notification de mission",
489 "La mission est visible dans la "
490 "zone de message",
491 1, "Oui", NULL, NULL);
492 #endif
493 }
494 else
495 /* Popup telling what happened */
496 #ifdef ENGLISH
497 (void)UTIL_PopupDialog("Mission Notification",
498 "To view your mission, click on\n"
499 " mission's button",
500 1, "Ok", NULL, NULL);
501 #endif
502 #ifdef FRENCH
503 (void)UTIL_PopupDialog("Notification de mission",
504 "Pour voir la mission, il faut \n"
505 "utiliser le bouton mission",
506 1, "Oui", NULL, NULL);
507 #endif
508 }
509 }
510 break;
511
512 default: {
513 char buf[256];
514 #ifdef ENGLISH
515 snprintf(buf, sizeof(buf), "CALLBACKS: Unhandled message (%d)",
516 iMessType);
517 (void)UTIL_PopupDialog("Fatal Error", buf, 1, "Ok", NULL, NULL);
518 #endif
519 #ifdef FRENCH
520 snprintf(buf, sizeof(buf), "CALLBACKS: message non g�r�(%d)",
521 iMessType);
522 (void)UTIL_PopupDialog("Erreur fatale", buf, 1, "Ok", NULL, NULL);
523 #endif
524 RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
525 MSG_DEREGISTERCLIENT, NULL);
526 UTIL_ExitProgram(-1);
527 }
528 }
529 }
530
531
532 /************************************************************************
533 * FUNCTION: CBK_RefreshMap
534 * HISTORY:
535 * 01.27.94 ESF Created.
536 * 01.28.94 ESF Changed to work with pixmap.
537 * PURPOSE:
538 * NOTES:
539 ************************************************************************/
540 void CBK_RefreshMap(Widget w, XtPointer pData, XtPointer pCalldata)
541 {
542 XExposeEvent *pExpose = (XExposeEvent *)pCalldata;
543 UNUSED(w);
544 UNUSED(pData);
545
546 XCopyArea(hDisplay, pixMapImage, hWindow, hGC,
547 pExpose->x, pExpose->y, pExpose->width, pExpose->height,
548 pExpose->x, pExpose->y);
549 }
550
551
552 #define DOUBLE_CLICK_TIME 300
553
554 /************************************************************************
555 * FUNCTION: CBK_MapClick
556 * HISTORY:
557 * 01.27.94 ESF Created
558 * 02.03.94 ESF Don't allocate colors in order, use mapping.
559 * 03.03.94 ESF Added some game glue.
560 * 03.07.94 ESF Made a lean, mean, fighting function. Look in game.c.
561 * 03.17.94 ESF Fixed remote case.
562 * 05.10.94 ESF Fixed to do nothing unless game has started.
563 * 10.02.94 ESF Added PLACE_ALL option.
564 * 06.09.95 JC Remember on click in fortify mode.
565 * 12.29.96 BPK Added multiple armies placement in fortification
566 * 01.17.00 ThH Now GAME_PlaceClick decides about number placed during fortify
567 * PURPOSE:
568 * NOTES: Handle click on map
569 ************************************************************************/
570 void CBK_MapClick(Widget w, XEvent *pEvent, String *str, Cardinal *card)
571 {
572 Int32 x, y, iCountry;
573 char buf[256];
574 UNUSED(w);
575 UNUSED(str);
576 UNUSED(card);
577
578 if (iState == STATE_REGISTER)
579 return;
580
581 /* Find out the coordinates and time of the mouse click */
582 x = pEvent->xbutton.x;
583 y = pEvent->xbutton.y;
584
585 /* Find out what country was clicked on */
586 iCountry = COLOR_ColorToCountry(XGetPixel(pMapImage, x, y));
587
588 if (fPlayingRemotely)
589 {
590 if ( (UTIL_NumPlayersAtClient(CLNT_GetThisClientID()) == 1)
591 && (iState == STATE_FORTIFY) && (iCountry < NUM_COUNTRIES))
592 {
593 if (iCountryToFortify == -1)
594 {
595 iCountryToFortify = iCountry;
596 snprintf(buf, sizeof(buf), "%s %s",
597 RISK_GetNameOfCountry(iCountryToFortify),
598 TXT_BE_FORTIFIED);
599 UTIL_DisplayError(buf);
600 return;
601 }
602 }
603 #ifdef ENGLISH
604 snprintf(buf, sizeof(buf), "Click as you may, it's %s's turn...",
605 #endif
606 #ifdef FRENCH
607 snprintf(buf, sizeof(buf), "Jouez avec le bouton, mais c'est le tour de %s...",
608 #endif
609 RISK_GetNameOfPlayer(iCurrentPlayer));
610 UTIL_DisplayError(buf);
611 return;
612 }
613
614 /* Erase previous errors */
615 UTIL_DisplayError("");
616
617 switch (iState)
618 {
619
620 case STATE_FORTIFY:
621 /*
622 * Changed so we can place multiple armies during fortification
623 * -- BPK
624 * -- TdH GAME_PlaceClick takes care of it now
625 * instead fall through */
626
627 case STATE_PLACE:
628 if (pEvent->xbutton.button == Button1)
629 GAME_PlaceClick(iCountry, PLACE_ONE);
630 else if (pEvent->xbutton.button == Button2)
631 GAME_PlaceClick(iCountry, PLACE_ALL);
632 else if (pEvent->xbutton.button == Button3)
633 GAME_PlaceClick(iCountry, PLACE_MULTIPLE);
634 break;
635
636 case STATE_ATTACK:
637 GAME_AttackClick(iCountry);
638 break;
639
640 case STATE_MOVE:
641 GAME_MoveClick(iCountry);
642 break;
643
644 default:
645 D_Assert(FALSE, "Shouldn't be here!");
646 }
647 }
648
649
650 /************************************************************************
651 * FUNCTION: CBK_Quit
652 * HISTORY:
653 * 01.29.94 ESF Created
654 * 04.02.94 ESF Added confirmation.
655 * 08.28.94 ESF Fixed to implement cleaner exit.
656 * 15.01.00 MSH Fixed to include Cancel button in English dialog
657 * PURPOSE:
658 * NOTES:
659 ************************************************************************/
660 void CBK_Quit(Widget w, XtPointer pData, XtPointer call_data)
661 {
662 Int32 rep;
663 UNUSED(w);
664 UNUSED(pData);
665 UNUSED(call_data);
666
667 #ifdef ENGLISH
668 rep = UTIL_PopupDialog("Quit", "Quit Frisk or new game?", 3, "Quit", "New", "Cancel");
669 #endif
670 #ifdef FRENCH
671 rep = UTIL_PopupDialog("Quit", "Quitter Frisk ou nouvelle partie?", 3, "Quitter", "Nouvelle", "Annuler");
672 #endif
673 switch (rep)
674 {
675 case QUERY_YES:
676 {
677 UTIL_ExitProgram(0);
678 }
679 case QUERY_NO:
680 {
681 iState = STATE_REGISTER;
682 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
683 MSG_ENDOFGAME, NULL);
684 }
685 }
686 }
687
688
689 /************************************************************************
690 * FUNCTION: CBK_ShowCards
691 * HISTORY:
692 * 02.19.94 ESF Created.
693 * 03.17.94 ESF Erase the error display.
694 * 05.04.94 ESF Fixed so that only the owner can see his or her cards.
695 * 05.10.94 ESF Fixed to do nothing unless game has started.
696 * 05.12.94 ESF Fixed to display correct cards.
697 * PURPOSE:
698 * NOTES:
699 ************************************************************************/
700 void CBK_ShowCards(Widget w, XtPointer pData, XtPointer call_data)
701 {
702 Int32 i, iPlayer;
703 UNUSED(w);
704 UNUSED(pData);
705 UNUSED(call_data);
706
707 #ifdef CARD_DEBUG
708 static Int32 j;
709 #endif
710
711 if (iState == STATE_REGISTER)
712 return;
713
714 UTIL_DisplayError("");
715
716 /* If the player clicking is the current player, show cards, otherwise,
717 * if there's more than one player at the client, issue an error, unless
718 * there's only one, in which case show his or her cards.
719 */
720
721 if (UTIL_PlayerIsLocal(iCurrentPlayer))
722 iPlayer = iCurrentPlayer;
723 else if (UTIL_NumPlayersAtClient(CLNT_GetThisClientID()) == 1)
724 iPlayer = RISK_GetNthPlayerAtClient(CLNT_GetThisClientID(), 0);
725 else
726 {
727 iPlayer = -1;
728 #ifdef ENGLISH
729 (void)UTIL_PopupDialog("Error", "Wait until your turn!",
730 1, "Ok", "Cancel", NULL);
731 #endif
732 #ifdef FRENCH
733 (void)UTIL_PopupDialog("Erreur", "Il faut attendre son tour!",
734 1, "Ok", "Annule", NULL);
735 #endif
736 }
737
738 /* If it's valid, display cards */
739 if (iPlayer >= 0)
740 {
741 XtPopup(wCardShell, XtGrabExclusive);
742
743 #ifdef CARD_DEBUG
744 for (i=0; i!=MAX_CARDS; i++)
745 {
746 CARD_RenderCard(j, i);
747 j = (++j) % NUM_CARDS;
748 }
749 #else
750 for (i=0; i!=RISK_GetNumCardsOfPlayer(iPlayer); i++)
751 CARDS_RenderCard(RISK_GetCardOfPlayer(iPlayer, i), i);
752 #endif
753 }
754 }
755
756
757 /************************************************************************
758 * FUNCTION: CBK_CancelCards
759 * HISTORY:
760 * 02.19.94 ESF Created.
761 * 03.17.94 ESF Added clearing of selections and unmappings.
762 * 04.01.94 ESF Fixed clearing of error when player cancels.
763 * 04.11.94 ESF Added not popping down the shell when !fForceExchange.
764 * PURPOSE:
765 * NOTES:
766 ************************************************************************/
767 void CBK_CancelCards(Widget w, XtPointer pData, XtPointer call_data)
768 {
769 Int32 i;
770 UNUSED(w);
771 UNUSED(pData);
772 UNUSED(call_data);
773
774 if (fForceExchange == TRUE)
775 #ifdef ENGLISH
776 (void)UTIL_PopupDialog("Error", "You must exchange cards!", 1,
777 "Ok", NULL, NULL);
778 #endif
779 #ifdef FRENCH
780 (void)UTIL_PopupDialog("Erreur", "Il faut �changer des cartes!", 1,
781 "Ok", NULL, NULL);
782 #endif
783 else
784 {
785 UTIL_DisplayError("");
786 XtPopdown(wCardShell);
787
788 /* Unmap the cards and clear the selections */
789 for (i=0; i!=MAX_CARDS; i++)
790 {
791 XtUnmanageChild(wCardToggle[i]);
792 XtVaSetValues(wCardToggle[i], XtNstate, False, NULL);
793 }
794 }
795 }
796
797
798 /************************************************************************
799 * FUNCTION: CBK_ShowMission
800 * HISTORY:
801 * 16.08.95 JC Created.
802 * PURPOSE:
803 * NOTES:
804 ************************************************************************/
805 void CBK_ShowMission(Widget w, XtPointer pData, XtPointer call_data)
806 {
807 Int32 iPlayer;
808 UNUSED(w);
809 UNUSED(pData);
810 UNUSED(call_data);
811
812 if (iState == STATE_REGISTER)
813 return;
814
815 UTIL_DisplayError("");
816
817 /* If the player clicking is the current player, show cards, otherwise,
818 * if there's more than one player at the client, issue an error, unless
819 * there's only one, in which case show his or her cards.
820 */
821
822 if (UTIL_PlayerIsLocal(iCurrentPlayer))
823 iPlayer = iCurrentPlayer;
824 else if (UTIL_NumPlayersAtClient(CLNT_GetThisClientID()) == 1)
825 iPlayer = RISK_GetNthPlayerAtClient(CLNT_GetThisClientID(), 0);
826 else
827 {
828 iPlayer = -1;
829 #ifdef ENGLISH
830 (void)UTIL_PopupDialog("Error", "Wait until your turn!",
831 1, "Ok", NULL, NULL);
832 #endif
833 #ifdef FRENCH
834 (void)UTIL_PopupDialog("Erreur", "Il faut attendre son tour!",
835 1, "Ok", NULL, NULL);
836 #endif
837 }
838
839 /* If it's valid, display mission */
840 if (iPlayer >= 0)
841 {
842 if (RISK_GetMissionTypeOfPlayer(iPlayer) == NO_MISSION)
843 {
844 #ifdef ENGLISH
845 if(UTIL_PopupDialog("Mission", "Play with a mission?",
846 2, "Ok", "Cancel", NULL) == QUERY_NO)
847 #endif
848 #ifdef FRENCH
849 if(UTIL_PopupDialog("Mission", "Jouer avec une mission?",
850 2, "Oui", "Annule", NULL) == QUERY_NO)
851 #endif
852 return;
853 fWaitMission = TRUE;
854 (void)RISK_SendSyncMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
855 MSG_MISSION, NULL,
856 MSG_MISSION, CBK_IncomingMessage);
857 fWaitMission = FALSE;
858 }
859 GAME_ShowMission(iPlayer);
860 }
861 }
862
863
864 /************************************************************************
865 * FUNCTION: CBK_Attack
866 * HISTORY:
867 * 03.03.94 ESF Stubbed.
868 * 03.05.94 ESF Coded.
869 * 05.07.94 ESF Modified to work with Distributed RiskGame Object.
870 * 05.10.94 ESF Fixed to do nothing unless game has started.
871 * 05.19.94 ESF Fixed bug, not refering to DiceMode.
872 * 10.15.94 ESF Added flexibility.
873 * 01.17.95 ESF Free pItem.
874 * PURPOSE:
875 * NOTES:
876 ************************************************************************/
877 void CBK_Attack(Widget w, XtPointer pData, XtPointer call_data)
878 {
879 XawListReturnStruct *pItem;
880 UNUSED(w);
881 UNUSED(pData);
882 UNUSED(call_data);
883
884 if (iState == STATE_REGISTER)
885 return;
886
887 /* Don't do anything if current player is not local and there is not
888 * one player at this client.
889 */
890
891 if (UTIL_PlayerIsLocal(iCurrentPlayer) == FALSE)
892 return;
893
894 /* I know this is silly, but hey... (We should get the data from pData?) */
895 pItem = XawListShowCurrent(wAttackList);
896 RISK_SetDiceModeOfPlayer(iCurrentPlayer, pItem->list_index);
897
898 /* Free up memory */
899 XtFree((void *)pItem);
900 }
901
902
903 /************************************************************************
904 * FUNCTION: CBK_Action
905 * HISTORY:
906 * 03.03.94 ESF Stubbed.
907 * 03.05.94 ESF Coded.
908 * 03.29.94 ESF Added player attack mode history.
909 * 05.07.94 ESF Modified to work with Distributed RiskGame Object.
910 * 05.10.94 ESF Fixed to do nothing unless game has started.
911 * 11.19.94 ESF Added a few UTIL_DisplayError("")'s, where needed.
912 * 01.17.95 ESF Free pItem.
913 * PURPOSE:
914 * NOTES:
915 ************************************************************************/
916 void CBK_Action(Widget w, XtPointer pData, XtPointer call_data)
917 {
918 XawListReturnStruct *pItem;
919 UNUSED(w);
920 UNUSED(pData);
921 UNUSED(call_data);
922
923 if (iState == STATE_REGISTER)
924 return;
925
926 /* Don't do anything if current player is not local */
927 if (!UTIL_PlayerIsLocal(iCurrentPlayer))
928 return;
929
930 /* I know this is silly, but hey... (We should get the data from pData?) */
931 pItem = XawListShowCurrent(wActionList);
932
933 switch (pItem->list_index)
934 {
935 /*****************/
936 case ACTION_PLACE:
937 /*****************/
938 if (iState == STATE_PLACE || iState == STATE_FORTIFY)
939 {
940 UTIL_DisplayError("");
941 iActionState = pItem->list_index;
942 }
943 else /* Invalid */
944 {
945 #ifdef ENGLISH
946 UTIL_DisplayError("You've already placed your armies.");
947 #endif
948 #ifdef FRENCH
949 UTIL_DisplayError("Toutes vos arm�es sont d�j� plac�e.");
950 #endif
951 XawListHighlight(wActionList, ACTION_ATTACK);
952 }
953
954 break;
955
956 /*******************/
957 case ACTION_ATTACK:
958 case ACTION_DOORDIE:
959 /*******************/
960 if (iState == STATE_ATTACK)
961 {
962 UTIL_DisplayError("");
963 iActionState = pItem->list_index;
964
965 /* Keep track of the type of attack selected */
966 RISK_SetAttackModeOfPlayer(iCurrentPlayer, pItem->list_index);
967 }
968 else if (iState == STATE_PLACE || iState == STATE_FORTIFY)
969 {
970 #ifdef ENGLISH
971 UTIL_DisplayError("You must finish placing your armies.");
972 #endif
973 #ifdef FRENCH
974 UTIL_DisplayError("Il faut finir de placer vos arm�es.");
975 #endif
976 XawListHighlight(wActionList, ACTION_PLACE);
977 }
978 else if (iState == STATE_MOVE) /* Must not have moved yet */
979 {
980 /* Keep track of the type of attack selected */
981 RISK_SetAttackModeOfPlayer(iCurrentPlayer, pItem->list_index);
982
983 iState = STATE_ATTACK;
984 UTIL_DisplayError("");
985 UTIL_ServerEnterState(iState);
986 UTIL_DisplayActionCString(iState, iCurrentPlayer);
987 }
988
989 break;
990
991 /****************/
992 case ACTION_MOVE:
993 /****************/
994 if (iState == STATE_MOVE)
995 {
996 UTIL_DisplayError("");
997 iActionState = pItem->list_index;
998 }
999 else if (iState == STATE_FORTIFY || iState == STATE_PLACE)
1000 {
1001 #ifdef ENGLISH
1002 UTIL_DisplayError("You must finish placing your armies.");
1003 #endif
1004 #ifdef FRENCH
1005 UTIL_DisplayError("Il faut finir de placer vos arm�es.");
1006 #endif
1007 XawListHighlight(wActionList, ACTION_PLACE);
1008 }
1009 else /* STATE_ATTACK */
1010 {
1011 iState = STATE_MOVE;
1012 UTIL_DisplayError("");
1013 UTIL_ServerEnterState(iState);
1014 UTIL_DisplayActionCString(iState, iCurrentPlayer);
1015 }
1016 break;
1017
1018 default:
1019 D_Assert(FALSE, "Shouldn't be here -- bogus state!");
1020 }
1021
1022 /* Free up memory */
1023 XtFree((void *)pItem);
1024 }
1025
1026
1027 /************************************************************************
1028 * FUNCTION: CBK_MsgDest
1029 * HISTORY:
1030 * 03.03.94 ESF Stubbed.
1031 * 05.07.94 ESF Created and modified to work with Distributed Object.
1032 * 05.19.94 ESF Fixed a bug, wasn't refering to MsgDestList.
1033 * 01.17.95 ESF Free pItem.
1034 * PURPOSE:
1035 * NOTES:
1036 ************************************************************************/
1037 void CBK_MsgDest(Widget w, XtPointer pData, XtPointer call_data)
1038 {
1039 XawListReturnStruct *pItem;
1040 Int32 iPlayer;
1041 UNUSED(w);
1042 UNUSED(pData);
1043 UNUSED(call_data);
1044
1045 if (UTIL_PlayerIsLocal(iCurrentPlayer))
1046 iPlayer = iCurrentPlayer;
1047 else if (UTIL_NumPlayersAtClient(CLNT_GetThisClientID()) == 1)
1048 iPlayer = RISK_GetNthPlayerAtClient(CLNT_GetThisClientID(), 0);
1049 else
1050 iPlayer = -1;
1051 if (iPlayer == -1)
1052 return;
1053
1054 /* I know this is silly, but hey... (We should get the data from pData?) */
1055 pItem = XawListShowCurrent(wMsgDestList);
1056 RISK_SetMsgDstModeOfPlayer(iPlayer, pItem->list_index);
1057
1058 /* Free up memory */
1059 XtFree((void *)pItem);
1060 }
1061
1062
1063 /************************************************************************
1064 * FUNCTION: CBK_SendMessage
1065 * HISTORY:
1066 * 05.03.94 ESF Created.
1067 * 05.04.94 ESF Enhanced.
1068 * 10.02.94 ESF Moved here and fixed a few (serious) bugs.
1069 * 01.09.95 ESF Changed to name sender if current player on client.
1070 * 01.17.95 ESF Free pItem.
1071 * 29.08.95 JC strFrom -> iFrom, iTo now contain the iPlayer.
1072 * PURPOSE:
1073 * NOTES:
1074 ************************************************************************/
1075 void CBK_SendMessage(void)
1076 {
1077 CString strMess;
1078 XawListReturnStruct *pItem;
1079 MsgMessagePacket mess;
1080
1081 /* Get the message and destination from widgets */
1082 XtVaGetValues(wSendMsgText, XtNstring, &strMess, NULL);
1083 pItem = XawListShowCurrent(wMsgDestList);
1084
1085 /* Who's sending the message? */
1086 if (UTIL_NumPlayersAtClient(CLNT_GetThisClientID()) == 1)
1087 mess.iFrom = RISK_GetNthPlayerAtClient(CLNT_GetThisClientID(), 0);
1088 else if (RISK_GetClientOfPlayer(iCurrentPlayer) == CLNT_GetThisClientID())
1089 mess.iFrom = iCurrentPlayer;
1090 else
1091 mess.iFrom = FROM_UNKNOW;
1092
1093 /* Who do we send the message to? */
1094 if (pItem->list_index == -1 || pItem->list_index == 0)
1095 mess.iTo = DST_ALLPLAYERS;
1096 else
1097 {
1098 mess.iTo = piMsgDstPlayerID[pItem->list_index];
1099 D_Assert(mess.iTo!=-1, "Bogus player!");
1100 }
1101
1102 mess.strMessage = strMess;
1103
1104 /* Display the message locally if necessary */
1105 if (mess.iTo != DST_ALLPLAYERS &&
1106 RISK_GetClientOfPlayer(mess.iTo) != CLNT_GetThisClientID()) {
1107 UTIL_DisplayMessage(mess.iFrom, mess.iTo, mess.strMessage);
1108 }
1109 /* Send the message */
1110 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1111 MSG_MESSAGEPACKET, &mess);
1112
1113 /* Erase the old message */
1114 XtVaSetValues(wSendMsgText, XtNstring, "", NULL);
1115
1116 /* Free up memory */
1117 XtFree((void *)pItem);
1118 }
1119
1120
1121 /************************************************************************
1122 * FUNCTION: CBK_CancelAttack
1123 * HISTORY:
1124 * 03.03.94 ESF Stubbed.
1125 * 03.04.94 ESF Coded.
1126 * 05.10.94 ESF Fixed to do nothing unless game has started.
1127 * PURPOSE:
1128 * NOTES:
1129 ************************************************************************/
1130 void CBK_CancelAttack(Widget w, XtPointer pData, XtPointer call_data)
1131 {
1132 UNUSED(w);
1133 UNUSED(pData);
1134 UNUSED(call_data);
1135
1136 if (iState == STATE_REGISTER)
1137 return;
1138
1139 if (fPlayingRemotely)
1140 {
1141 #ifdef ENGLISH
1142 UTIL_DisplayError("Nice try, but it isn't your turn...");
1143 #endif
1144 #ifdef FRENCH
1145 UTIL_DisplayError("Bien essay�, mais ce n'est pas votre tour...");
1146 #endif
1147 return;
1148 }
1149
1150 UTIL_DisplayError("");
1151
1152 if (iState == STATE_ATTACK)
1153 {
1154 if (GAME_AttackFrom() >=0)
1155 UTIL_DarkenCountry(GAME_AttackFrom());
1156 GAME_SetAttackSrc(-1);
1157 }
1158 else if (iState == STATE_MOVE)
1159 {
1160 if (iMoveSrc >=0)
1161 UTIL_DarkenCountry(iMoveSrc);
1162 iMoveSrc = -1;
1163 }
1164
1165 UTIL_DisplayActionCString(iState, iCurrentPlayer);
1166 }
1167
1168
1169 /************************************************************************
1170 * FUNCTION: CBK_Repeat
1171 * HISTORY:
1172 * 03.03.94 ESF Stubbed.
1173 * 03.06.94 ESF Coded.
1174 * 03.07.94 ESF Fixed state bug.
1175 * 03.08.94 ESF Added DOORDIE recognition.
1176 * 03.16.94 ESF Fixed bug in DOORDIE.
1177 * 04.02.94 ESF Added server notification caching for do-or-die.
1178 * 05.10.94 ESF Fixed to do nothing unless game has started.
1179 * 05.19.94 ESF Added verbose message across network.
1180 * PURPOSE:
1181 * NOTES:
1182 ************************************************************************/
1183 void CBK_Repeat(Widget w, XtPointer pData, XtPointer call_data)
1184 {
1185 MsgNetMessage mess;
1186 char buf[256];
1187 UNUSED(w);
1188 UNUSED(pData);
1189 UNUSED(call_data);
1190
1191 if (iState == STATE_REGISTER)
1192 return;
1193
1194 if (fPlayingRemotely)
1195 {
1196 #ifdef ENGLISH
1197 snprintf(buf, sizeof(buf), "I'm afraid that's up to %s to decide...",
1198 #endif
1199 #ifdef FRENCH
1200 snprintf(buf, sizeof(buf),
1201 "Je suis effray� par ce que vous d�cidez pour %s...",
1202 #endif
1203 RISK_GetNameOfPlayer(iCurrentPlayer));
1204 UTIL_DisplayError(buf);
1205 return;
1206 }
1207
1208 if (iState != STATE_ATTACK)
1209 #ifdef ENGLISH
1210 UTIL_DisplayError("You can't do any attacking at the moment.");
1211 #endif
1212 #ifdef FRENCH
1213 UTIL_DisplayError("Il n'y a rien � faire pour l'instant.");
1214 #endif
1215 else if (iActionState == ACTION_ATTACK || iActionState == ACTION_DOORDIE)
1216 {
1217 if (iLastAttackSrc == -1 || iLastAttackDst == -1)
1218 {
1219 #ifdef ENGLISH
1220 UTIL_DisplayError("You can't repeat an uncomplete attack.");
1221 #endif
1222 #ifdef FRENCH
1223 UTIL_DisplayError("Impossible de r�p�ter une attaque incompl�te.");
1224 #endif
1225 return;
1226 }
1227
1228 /* send a verbose message */
1229 #ifdef ENGLISH
1230 snprintf(buf, sizeof(buf), "%s is attacking from %s to %s",
1231 RISK_GetNameOfPlayer(iCurrentPlayer),
1232 RISK_GetNameOfCountry(iLastAttackSrc),
1233 RISK_GetNameOfCountry(iLastAttackDst));
1234 #endif
1235 #ifdef FRENCH
1236 snprintf(buf, sizeof(buf), "%s attaque %s depuis %s",
1237 RISK_GetNameOfPlayer(iCurrentPlayer),
1238 RISK_GetNameOfCountry(iLastAttackDst),
1239 RISK_GetNameOfCountry(iLastAttackSrc));
1240 #endif
1241 mess.strMessage = buf;
1242 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1243 MSG_NETMESSAGE, &mess);
1244
1245 /* Repeat the attack */
1246 GAME_Attack(iLastAttackSrc, iLastAttackDst);
1247 }
1248 }
1249
1250
1251 /************************************************************************
1252 * FUNCTION: CBK_Help
1253 * HISTORY:
1254 * 04.01.94 ESF Coded.
1255 * PURPOSE:
1256 * NOTES:
1257 ************************************************************************/
1258 void CBK_Help(Widget w, XtPointer pData, XtPointer call_data)
1259 {
1260 Int32 x, y;
1261 UNUSED(w);
1262 UNUSED(pData);
1263 UNUSED(call_data);
1264
1265 /* Center popup */
1266 UTIL_CenterShell(wHelpShell, wToplevel, &x, &y);
1267 XtVaSetValues(wHelpShell,
1268 XtNallowShellResize, False,
1269 XtNx, x,
1270 XtNy, y,
1271 XtNborderWidth, 1,
1272 #ifdef ENGLISH
1273 XtNtitle, "Frisk Help",
1274 #endif
1275 #ifdef FRENCH
1276 XtNtitle, "Aide de frisk",
1277 #endif
1278 NULL);
1279
1280 XtPopup(wHelpShell, XtGrabNone);
1281 }
1282
1283
1284 /************************************************************************
1285 * FUNCTION: CBK_HelpSelectTopic
1286 * HISTORY:
1287 * 04.01.94 ESF Coded.
1288 * 01.17.95 ESF Free pItem.
1289 * PURPOSE:
1290 * NOTES:
1291 ************************************************************************/
1292 void CBK_HelpSelectTopic(Widget w, XtPointer pData, XtPointer call_data)
1293 {
1294 XawListReturnStruct *pItem;
1295 UNUSED(w);
1296 UNUSED(pData);
1297 UNUSED(call_data);
1298
1299 /* I know this is sill, but hey... (We should get the data from pData) */
1300 pItem = XawListShowCurrent(wHelpTopicList);
1301 HELP_IndexPopupHelp(pItem->list_index);
1302
1303 /* Free up memory */
1304 XtFree((void *)pItem);
1305 }
1306
1307
1308 /************************************************************************
1309 * FUNCTION: CBK_HelpOk
1310 * HISTORY:
1311 * 04.01.94 ESF Coded.
1312 * PURPOSE:
1313 * NOTES:
1314 ************************************************************************/
1315 void CBK_HelpOk(Widget w, XtPointer pData, XtPointer call_data)
1316 {
1317 UNUSED(w);
1318 UNUSED(pData);
1319 UNUSED(call_data);
1320 XtPopdown(wHelpShell);
1321 }
1322
1323
1324 /************************************************************************
1325 * FUNCTION: CBK_ExchangeCards
1326 * HISTORY:
1327 * 03.03.94 ESF Stubbed.
1328 * 03.29.94 ESF Coded.
1329 * 05.03.94 ESF Fixed for when the player has many cards.
1330 * 02.21.95 ESF Fixed a bug -- only current player may exchange.
1331 * PURPOSE:
1332 * NOTES:
1333 ************************************************************************/
1334 void CBK_ExchangeCards(Widget w, XtPointer pData, XtPointer call_data)
1335 {
1336 Int32 i, iNumCardsSelected;
1337 Flag iCardState;
1338 Int32 piCards[3], piCardValues[3], piCardTypes[3];
1339 UNUSED(w);
1340 UNUSED(pData);
1341 UNUSED(call_data);
1342
1343 /* If it's not the current player, then don't let the exchange
1344 * take place. This fixes a nasty bug reported by many.
1345 */
1346
1347 if (RISK_GetClientOfPlayer(iCurrentPlayer) != CLNT_GetThisClientID())
1348 UTIL_DisplayError(TXT_ONLY_CURRENT);
1349 /* Get the cards that have been pressed, check for validity */
1350 for (i=iNumCardsSelected=0; i!=MAX_CARDS; i++)
1351 {
1352 XtVaGetValues(wCardToggle[i], XtNstate, &iCardState, NULL);
1353 if (iCardState == True)
1354 {
1355 if (iNumCardsSelected<3)
1356 piCards[iNumCardsSelected] = i;
1357 iNumCardsSelected++;
1358 }
1359 }
1360
1361 /* Clear the selections of the cards */
1362 for (i=0; i!=MAX_CARDS; i++)
1363 XtVaSetValues(wCardToggle[i], XtNstate, False, NULL);
1364
1365 /* Valid number of cards selected */
1366 if (fCanExchange == FALSE)
1367 {
1368 #ifdef ENGLISH
1369 UTIL_DisplayError("You can only exchange cards at the beginning of "
1370 "your turn.");
1371 #endif
1372 #ifdef FRENCH
1373 UTIL_DisplayError("Les cartes ne sont �changeables qu'au d�but de "
1374 "votre tour.");
1375 #endif
1376 return;
1377 }
1378 else if (iNumCardsSelected!=3)
1379 {
1380 #ifdef ENGLISH
1381 UTIL_DisplayError("You must select three cards to exchange.");
1382 #endif
1383 #ifdef FRENCH
1384 UTIL_DisplayError("Il faut s�lectionner trois cartes pour pouvoir "
1385 "�changer.");
1386 #endif
1387 return;
1388 }
1389
1390 /* Substitute the indices for the card values */
1391 for (i=0; i!=3; i++)
1392 {
1393 piCardValues[i] = RISK_GetCardOfPlayer(iCurrentPlayer, piCards[i]);
1394
1395 /* Set the type of the card */
1396 if (piCardValues[i]<NUM_COUNTRIES)
1397 piCardTypes[i] = piCardValues[i] % 3;
1398 else
1399 piCardTypes[i] = -1; /* Joker */
1400 }
1401
1402 /* Now see if the cards form a valid triple */
1403 if ((piCardTypes[0]==piCardTypes[1] &&
1404 piCardTypes[1]==piCardTypes[2] &&
1405 piCardTypes[0]==piCardTypes[2]) ||
1406 (piCardTypes[0]!=piCardTypes[1] &&
1407 piCardTypes[1]!=piCardTypes[2] &&
1408 piCardTypes[0]!=piCardTypes[2]) ||
1409 (piCardTypes[0]==-1 || piCardTypes[1]==-1 || piCardTypes[2]==-1))
1410 {
1411 /* Actually perform the exchange */
1412 GAME_ExchangeCards(piCards);
1413
1414 /* If this is the only exchange, then pop down the window */
1415 if (RISK_GetNumCardsOfPlayer(iCurrentPlayer) <= 5)
1416 {
1417 /* We're through exchanging sets of cards */
1418 fForceExchange = FALSE;
1419
1420 XtPopdown(wCardShell);
1421
1422 /* Unmap the cards and clear the selections */
1423 for (i=0; i!=MAX_CARDS; i++)
1424 XtUnmanageChild(wCardToggle[i]);
1425 }
1426 else
1427 {
1428 /* Redisplay cards after erasing and unselecting the old ones */
1429 for (i=0; i!=MAX_CARDS; i++)
1430 {
1431 XtUnmanageChild(wCardToggle[i]);
1432 XtVaSetValues(wCardToggle[i], XtNstate, False, NULL);
1433 }
1434
1435 for (i=0; i!=RISK_GetNumCardsOfPlayer(iCurrentPlayer); i++)
1436 CARDS_RenderCard(RISK_GetCardOfPlayer(iCurrentPlayer, i), i);
1437 }
1438
1439 /* Display the appropriate message for the state */
1440 UTIL_DisplayActionCString(iState, iCurrentPlayer);
1441 }
1442 else
1443 #ifdef ENGLISH
1444 UTIL_DisplayError("You must pick three cards of the same type or one of "
1445 "each type.");
1446 #endif
1447 #ifdef FRENCH
1448 UTIL_DisplayError("Vous devez choisir trois cartes du m�me type ou une de "
1449 "chaque type.");
1450 #endif
1451 }
1452
1453
1454 /************************************************************************
1455 * FUNCTION: CBK_EndTurn
1456 * HISTORY:
1457 * 03.05.94 ESF Created.
1458 * 05.10.94 ESF Fixed to do nothing unless game has started.
1459 * PURPOSE:
1460 * NOTES:
1461 ************************************************************************/
1462 void CBK_EndTurn(Widget w, XtPointer pData, XtPointer call_data)
1463 {
1464 char buf[256];
1465 UNUSED(w);
1466 UNUSED(pData);
1467 UNUSED(call_data);
1468
1469 if (iState == STATE_REGISTER)
1470 return;
1471
1472 if (fPlayingRemotely)
1473 {
1474 #ifdef ENGLISH
1475 snprintf(buf, sizeof(buf), "Come on, let %s finish playing...",
1476 RISK_GetNameOfPlayer(iCurrentPlayer));
1477 #endif
1478 #ifdef FRENCH
1479 snprintf(buf, sizeof(buf), "Come on, let %s finish playing...",
1480 RISK_GetNameOfPlayer(iCurrentPlayer));
1481 #endif
1482 UTIL_DisplayError(buf);
1483 return;
1484 }
1485
1486 if (iState == STATE_PLACE || iState == STATE_FORTIFY)
1487 #ifdef ENGLISH
1488 UTIL_DisplayError("You must finish placing your armies.");
1489 #endif
1490 #ifdef FRENCH
1491 UTIL_DisplayError("Vous devez finir de placer vos arm�es.");
1492 #endif
1493 else
1494 {
1495 UTIL_DisplayError("");
1496 GAME_EndTurn();
1497 }
1498 }
1499
1500
1501 /************************************************************************
1502 * FUNCTION: CBK_RefreshDice
1503 * HISTORY:
1504 * 04.11.94 ESF Created.
1505 * PURPOSE:
1506 * NOTES:
1507 ************************************************************************/
1508 void CBK_RefreshDice(Widget w, XtPointer pData, XtPointer call_data)
1509 {
1510 UNUSED(w);
1511 UNUSED(pData);
1512 UNUSED(call_data);
1513
1514 DICE_Refresh();
1515 }
1516
1517
1518 /************************************************************************
1519 * FUNCTION: CBK_About
1520 * HISTORY:
1521 * 10.02.94 ESF Created.
1522 * PURPOSE:
1523 * NOTES:
1524 ************************************************************************/
1525 void CBK_About(Widget w, XtPointer pData, XtPointer call_data)
1526 {
1527 char buf[256];
1528 UNUSED(w);
1529 UNUSED(pData);
1530 UNUSED(call_data);
1531
1532 /* Print version information */
1533 #ifdef ENGLISH
1534 snprintf(buf, sizeof(buf), "This is Frisk version %s, compiled %s at %s",
1535 VERSION, __DATE__, __TIME__);
1536 #endif
1537 #ifdef FRENCH
1538 snprintf(buf, sizeof(buf),
1539 "Ceci est la version %s de Frisk, compil�e le %s � %s",
1540 VERSION, __DATE__, __TIME__);
1541 #endif
1542 UTIL_DisplayComment(buf);
1543 }
1544
1545
1546 /************************************************************************
1547 * FUNCTION: CBK_Replicate
1548 * HISTORY:
1549 * 02.26.95 ESF Created.
1550 * PURPOSE:
1551 * NOTES:
1552 ************************************************************************/
1553 void CBK_Replicate(Int32 iMessType, void *pvMess, Int32 iType, Int32 iSrc)
1554 {
1555 UNUSED(iSrc);
1556
1557 /*
1558 * Do the physical replication (send to the server so it will
1559 * broadcast it), but only if this callback is being called
1560 * for an outgoing message (i.e. a replication). Otherwise,
1561 * a message just came in.
1562 */
1563
1564 if (iType == MESS_OUTGOING)
1565 {
1566 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1567 iMessType, pvMess);
1568 }
1569 }
1570
1571
1572 /************************************************************************
1573 * FUNCTION: CBK_Callback
1574 * HISTORY:
1575 * 05.03.94 ESF Created.
1576 * 08.18.94 ESF Changed so that this does replication.
1577 * 01.09.95 ESF Changed to not handle player death messages.
1578 * 01.17.95 ESF Added to handle MsgDst changes entirely here.
1579 * 02.26.95 ESF Modified to look more like a controller in MVC.
1580 * PURPOSE:
1581 * NOTES:
1582 ************************************************************************/
1583 void CBK_Callback(Int32 iMessType, void *pvMess)
1584 {
1585 /* Based on the message from the Model, perform some sort of action */
1586 switch (iMessType)
1587 {
1588 case MSG_OBJINTUPDATE:
1589 {
1590 MsgObjIntUpdate *pMess = (MsgObjIntUpdate *)pvMess;
1591 switch(pMess->iField)
1592 {
1593 case CNT_OWNER:
1594 {
1595 /* Recolor the country */
1596 if (pMess->iNewValue >= 0)
1597 COLOR_ColorCountry(pMess->iIndex1, pMess->iNewValue);
1598 }
1599 break;
1600
1601 case CNT_NUMARMIES:
1602 {
1603 /* Redraw the number of armies on the country. Since the
1604 * country may be lit up because of the notification, we
1605 * take pains not to mess with it. Check to see if it was
1606 * lit up, and if so, leave it that way.
1607 */
1608
1609 UTIL_PrintArmies(pMess->iIndex1, pMess->iNewValue,
1610 CLNT_GetLightCountOfCountry(pMess->iIndex1)>0 ?
1611 WhitePixel(hDisplay, 0) :
1612 BlackPixel(hDisplay, 0));
1613 }
1614 break;
1615
1616 case PLR_ALLOCATION:
1617 {
1618 /* If a player was killed then remove it from the MsgDst list */
1619 if (pMess->iNewValue == ALLOC_NONE)
1620 {
1621 Int32 iDeadPlayer = pMess->iIndex1;
1622 Int32 i, iIndex;
1623 Flag fDone = FALSE;
1624
1625 /* Search for the entry to delete it */
1626 for (i=1; i!=iIndexMD && !fDone; i++)
1627 if (piMsgDstPlayerID[i] == iDeadPlayer)
1628 fDone = TRUE;
1629
1630 D_Assert(fDone, "Something's wierd!");
1631
1632 /* Cause we want to recover somewhat if not debugging */
1633 if (!fDone)
1634 break;
1635
1636 /* We found the entry. Delete it and move the others down */
1637 iIndex = (i-1);
1638
1639 MEM_Free(pstrMsgDstCString[iIndex]);
1640 piMsgDstPlayerID[iIndex] = -1;
1641 iIndexMD--;
1642
1643 for (i=iIndex; i<iIndexMD; i++)
1644 {
1645 pstrMsgDstCString[i] = pstrMsgDstCString[i+1];
1646 piMsgDstPlayerID[i] = piMsgDstPlayerID[i+1];
1647 }
1648
1649 UTIL_RefreshMsgDest(iIndexMD);
1650 }
1651 /* If the player was created, add it to the message box */
1652 else if (pMess->iNewValue == ALLOC_COMPLETE)
1653 {
1654 /* A new player, add this to the list, update listbox */
1655 char buf[256];
1656 pstrMsgDstCString[iIndexMD] =
1657 (CString)MEM_Alloc(strlen(
1658 RISK_GetNameOfPlayer(pMess->iIndex1))+1);
1659 piMsgDstPlayerID[iIndexMD] = pMess->iIndex1;
1660 strcpy(pstrMsgDstCString[iIndexMD],
1661 RISK_GetNameOfPlayer(pMess->iIndex1));
1662 UTIL_RefreshMsgDest(++iIndexMD);
1663
1664 /* Also create a message that tells of the new player */
1665 #ifdef ENGLISH
1666 snprintf(buf, sizeof(buf), "%s (%s) has joined.",
1667 #endif
1668 #ifdef FRENCH
1669 snprintf(buf, sizeof(buf), "%s (%s) est arriv�.",
1670 #endif
1671 RISK_GetNameOfPlayer(pMess->iIndex1),
1672 RISK_GetNameOfSpecies(
1673 RISK_GetSpeciesOfPlayer(pMess->iIndex1)));
1674 UTIL_DisplayMessage(FROM_SERVER, DST_OTHER, buf);
1675 }
1676 }
1677 break;
1678
1679 default:
1680 /* A message that we don't care about */
1681 ;
1682 }
1683 break;
1684 }
1685
1686 case MSG_OBJSTRUPDATE:
1687 {
1688 MsgObjStrUpdate *pMess = (MsgObjStrUpdate *)pvMess;
1689 switch (pMess->iField)
1690 {
1691 case PLR_COLORSTRING:
1692 {
1693 /* Allocate the color */
1694 COLOR_StoreNamedColor(pMess->strNewValue, pMess->iIndex1);
1695 }
1696 break;
1697
1698 default:
1699 /* A message that we don't care about */
1700 ;
1701 }
1702 }
1703 break;
1704
1705 default:
1706 /* Nothing... */
1707 ;
1708 }
1709 }
1710