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: game.c,v 1.13 2000/01/18 20:07:26 morphy Exp $
20 *
21 * $Log: game.c,v $
22 * Revision 1.13 2000/01/18 20:07:26 morphy
23 * Made middle button (drop all armies) obey DROP_ARMIES limitation
24 *
25 * Revision 1.12 2000/01/18 19:56:43 morphy
26 * Prevention of dropping all armies works now
27 *
28 * Revision 1.11 2000/01/18 09:25:47 tony
29 * oops, little braino in "dropping armies" code
30 *
31 * Revision 1.10 2000/01/17 21:09:03 tony
32 * small fix on dropping armies during fortification, optional number
33 *
34 */
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <unistd.h>
39
40 #include "registerPlayers.h"
41 #include "callbacks.h"
42 #include "utils.h"
43 #include "dice.h"
44 #include "game.h"
45 #include "debug.h"
46 #include "riskgame.h"
47 #include "types.h"
48 #include "colormap.h"
49 #include "client.h"
50
51
52 /* Globals */
53 static MsgNetMessage mess;
54
55 static Int32 iAttackDst=-1;
56 static Int32 iMoveDst=-1;
57
58 /*iAttackSrc is used by callbacks.c */
59 static Int32 iAttackSrc=-1;
60
61 Int32 iLastAttackSrc=-1, iLastAttackDst=-1;
62 Int32 iMoveSrc=-1;
63
64 Flag fGetsCard=FALSE;
65 Flag fCanExchange=TRUE;
66
67 /* max number of armies to allow when not using DROP_ALL */
68 #define DROP_ARMIES 3
69
70 /************************************************************************
71 * FUNCTION: GAME_AttackFrom
72 * HISTORY: 291099 Tdh
73 * PURPOSE: Return value of iAttackSrc
74 * NOTES: to make iAttackSrc private to game.c
75 */
GAME_AttackFrom()76 Int32 GAME_AttackFrom() {
77 return iAttackSrc;
78 }
79
80 /************************************************************************
81 * FUNCTION: GAME_SetAttackSrc
82 * HISTORY: 291099 Tdh
83 * PURPOSE: Set value of iAttackSrc
84 * NOTES: to make iAttackSrc private to game.c
85 */
86
GAME_SetAttackSrc(Int32 src)87 void GAME_SetAttackSrc(Int32 src) {
88 iAttackSrc = src;
89 }
90
91
92 /************************************************************************
93 * FUNCTION: GAME_CanAttack
94 * HISTORY:
95 * 03.04.94 ESF Created.
96 * PURPOSE: Check if this attack is possible
97 * NOTES:
98 ************************************************************************/
GAME_CanAttack(Int32 AttackSrc,Int32 iAttackDst)99 Flag GAME_CanAttack(Int32 AttackSrc, Int32 iAttackDst)
100 {
101 Int32 i;
102
103 for (i=0; i!=6 && RISK_GetAdjCountryOfCountry(AttackSrc, i)!=-1; i++)
104 if (RISK_GetAdjCountryOfCountry(AttackSrc, i) == iAttackDst)
105 return TRUE;
106
107 return FALSE;
108 }
109
110
111 /************************************************************************
112 * FUNCTION: GAME_SetTurnArmiesOfPlayer
113 * HISTORY:
114 * 04.23.95 ESF Created.
115 * PURPOSE:
116 * NOTES:
117 ************************************************************************/
GAME_SetTurnArmiesOfPlayer(Int32 iCurrentPlayer)118 void GAME_SetTurnArmiesOfPlayer(Int32 iCurrentPlayer)
119 {
120 Int32 iNumArmies=0;
121
122 /* Add the continent bonus */
123 iNumArmies += GAME_GetContinentBonus(iCurrentPlayer);
124
125 /* Add the standard "at-least-three-and-at-most-one-per-three-countries" */
126 iNumArmies += MAX(RISK_GetNumCountriesOfPlayer(iCurrentPlayer)/3, 3);
127
128 RISK_SetNumArmiesOfPlayer(iCurrentPlayer, iNumArmies);
129 }
130
131
132 /************************************************************************
133 * FUNCTION: GAME_MoveArmies
134 * HISTORY:
135 * 03.04.94 ESF Created.
136 * 03.18.94 ESF Added server update.
137 * 05.19.94 ESF Added verbose message across network.
138 * PURPOSE:
139 * NOTES:
140 ************************************************************************/
GAME_MoveArmies(Int32 iSrcCountry,Int32 iDstCountry,Int32 iNumArmies)141 void GAME_MoveArmies(Int32 iSrcCountry, Int32 iDstCountry, Int32 iNumArmies)
142 {
143 MsgMoveNotify msgMoveNotify;
144 char buf[256];
145
146 /* Notify the server */
147 msgMoveNotify.iSrcCountry = iSrcCountry;
148 msgMoveNotify.iDstCountry = iDstCountry;
149 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
150 MSG_MOVENOTIFY, &msgMoveNotify);
151
152 /* send a verbose message */
153 #ifdef ENGLISH
154 snprintf(buf, sizeof(buf), "%s moved %d armies from %s to %s",
155 #endif
156 #ifdef FRENCH
157 snprintf(buf, sizeof(buf), "%s d�place %d arm�e(s) de %s � %s",
158 #endif
159 RISK_GetNameOfPlayer(iCurrentPlayer),
160 iNumArmies,
161 RISK_GetNameOfCountry(iSrcCountry),
162 RISK_GetNameOfCountry(iDstCountry));
163 mess.strMessage = buf;
164 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
165 MSG_NETMESSAGE, &mess);
166
167 /* Update date structures */
168 RISK_SetNumArmiesOfCountry(iDstCountry,
169 RISK_GetNumArmiesOfCountry(iDstCountry) + iNumArmies);
170 RISK_SetNumArmiesOfCountry(iSrcCountry,
171 RISK_GetNumArmiesOfCountry(iSrcCountry) - iNumArmies);
172 }
173
174
175 /************************************************************************
176 * FUNCTION: GAME_PlaceArmies
177 * HISTORY:
178 * 03.04.94 ESF Stubbed.
179 * 05.19.94 ESF Coded and added verbose message across network.
180 * 10.02.94 ESF Added support for PLACE_ALL.
181 * 04.30.95 ESF Moved the dialog code from to PlaceClick.
182 * PURPOSE:
183 * NOTES:
184 ************************************************************************/
185 void GAME_PlaceArmies(Int32 iCountry, Int32 iArmies)
186 {
187 MsgPlaceNotify msgPlaceNotify;
188 char buf[256];
189
190 /* Send a message to the server about this */
191 msgPlaceNotify.iCountry = iCountry;
192 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
193 MSG_PLACENOTIFY, &msgPlaceNotify);
194
195 /* Send a verbose message */
196 #ifdef ENGLISH
197 snprintf(buf, sizeof(buf), "%s placing %d %s on %s",
198 #endif
199 #ifdef FRENCH
200 snprintf(buf, sizeof(buf), "%s place %d %s en %s",
201 #endif
202 RISK_GetNameOfPlayer(iCurrentPlayer),
203 iArmies,
204 iArmies == 1 ? "army" : "armies",
205 RISK_GetNameOfCountry(iCountry));
206 mess.strMessage = buf;
207 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
208 MSG_NETMESSAGE, &mess);
209
210 /* Once we've placed, can't exchange cards anymore */
211 fCanExchange = FALSE;
212
213 /* Update the data structures */
214 RISK_SetNumArmiesOfPlayer(iCurrentPlayer,
215 RISK_GetNumArmiesOfPlayer(iCurrentPlayer)
216 - iArmies);
217 RISK_SetNumArmiesOfCountry(iCountry,
218 RISK_GetNumArmiesOfCountry(iCountry)+iArmies);
219
220 /* If we're out of armies, jump into attack mode */
221 if (!RISK_GetNumArmiesOfPlayer(iCurrentPlayer) && iState == STATE_PLACE)
222 {
223 iState = STATE_ATTACK;
224 UTIL_ServerEnterState(iState);
225 UTIL_DisplayActionCString(iState, iCurrentPlayer);
226 }
227
228 UTIL_DisplayActionCString(iState, iCurrentPlayer);
229 }
230
231
232 /************************************************************************
233 * FUNCTION: GAME_Attack
234 * HISTORY:
235 * 04.23.95 ESF Created.
236 * PURPOSE:
237 * NOTES:
238 ************************************************************************/
239 void GAME_Attack(Int32 iSrcCountry, Int32 iDstCountry)
240 {
241 Int32 iActionState = RISK_GetAttackModeOfPlayer(iCurrentPlayer);
242 Int32 iDice = RISK_GetDiceModeOfPlayer(iCurrentPlayer);
243
244 /* Keep these so as to be able to repeat attack */
245 iLastAttackSrc = iSrcCountry;
246 iLastAttackDst = iDstCountry;
247
248 if (iActionState == ACTION_ATTACK)
249 {
250 if (GAME_ValidAttackSrc(iSrcCountry, TRUE) &&
251 GAME_ValidAttackDst(iSrcCountry, iDstCountry, TRUE) &&
252 GAME_ValidAttackDice(iDice, iSrcCountry))
253 {
254 GAME_DoAttack(iSrcCountry, iDstCountry, TRUE);
255 UTIL_DisplayActionCString(iState, iCurrentPlayer);
256 }
257 }
258 else if (iActionState == ACTION_DOORDIE)
259 {
260 Flag fNotify = TRUE;
261
262 while (GAME_ValidAttackSrc(iSrcCountry, FALSE) &&
263 GAME_ValidAttackDst(iSrcCountry, iDstCountry, FALSE) &&
264 GAME_ValidAttackDice(iDice, iSrcCountry))
265 {
266 GAME_DoAttack(iSrcCountry, iDstCountry, fNotify);
267
268 /* Only True the first time around */
269 fNotify = FALSE;
270 }
271 }
272 }
273
274
275 /************************************************************************
276 * FUNCTION: GAME_DoAttack
277 * HISTORY:
278 * 03.04.94 ESF Created.
279 * 03.06.94 ESF Added missing pieces.
280 * 03.07.94 ESF Fixed owner update bug & army subtraction bug.
281 * 03.18.94 ESF Added server notification.
282 * 03.28.94 ESF Added message when country is taken.
283 * 03.29.94 ESF Added more informative moving message.
284 * 04.02.94 ESF Fixed bug, darken country if wrong number of die.
285 * 04.02.94 ESF Only notify server if fCacheNotify == FALSE.
286 * 05.04.94 ESF Fixed bug, changed location of call to GAME_PlayerDied().
287 * 05.04.94 ESF Fixed bug, when end of game occurs.
288 * 05.19.94 ESF Added verbose notification when player takes country.
289 * 05.19.94 ESF Moved calculation of dice correctness to ValidDice.
290 * 11.06.94 ESF Cached results, so that Do-or-die runs quicker.
291 * 11.06.94 ESF Added attack notification.
292 * 01.17.95 ESF Changed fCacheNotify to fNotify.
293 * 07.09.95 JC Fixed a bug with ENDOFMISSION and FORCEEXCHANGECARDS.
294 * PURPOSE:
295 * NOTES:
296 ************************************************************************/
297 void GAME_DoAttack(Int32 iSrcCountry, Int32 iDstCountry, Flag fNotify)
298 {
299 MsgAttackNotify msgAttackNotify;
300 Int32 iAttackDie, iDefendDie, iArmiesWon, iArmiesMove;
301 Int32 iSrc, iDst, iPlayerAttackDie;
302 char buf[256];
303
304 /* Cached values */
305 static Int32 iCachedSrcCountry=-1;
306 static Int32 iCachedDstCountry=-1;
307
308 iPlayerAttackDie = RISK_GetDiceModeOfPlayer(iCurrentPlayer);
309
310 /* If the dice have been selected automatically, calculate them */
311 if (iPlayerAttackDie != ATTACK_AUTO)
312 iAttackDie = iPlayerAttackDie+1;
313 else
314 iAttackDie = MIN(RISK_GetNumArmiesOfCountry(iSrcCountry)-1, 3);
315
316 iDefendDie = MIN(RISK_GetNumArmiesOfCountry(iDstCountry), 2);
317
318 /* Set the color of the dice if the attack has changed */
319 if (iCachedSrcCountry != iSrcCountry)
320 COLOR_CopyColor(COLOR_PlayerToColor(RISK_GetOwnerOfCountry(iSrcCountry)),
321 COLOR_DieToColor(0));
322 if (iCachedDstCountry != iDstCountry)
323 COLOR_CopyColor(COLOR_PlayerToColor(RISK_GetOwnerOfCountry(iDstCountry)),
324 COLOR_DieToColor(1));
325
326 /* Get the number of armies that the attacker won */
327 iArmiesWon = DICE_Attack(iAttackDie, iDefendDie,
328 RISK_GetOwnerOfCountry(iSrcCountry),
329 RISK_GetOwnerOfCountry(iDstCountry));
330
331 RISK_SetNumArmiesOfCountry(iDstCountry,
332 RISK_GetNumArmiesOfCountry(iDstCountry) - iArmiesWon);
333 RISK_SetNumArmiesOfCountry(iSrcCountry,
334 RISK_GetNumArmiesOfCountry(iSrcCountry) -
335 (MIN(iDefendDie, iAttackDie) - iArmiesWon));
336
337 /* Country was taken */
338 if (!RISK_GetNumArmiesOfCountry(iDstCountry))
339 {
340 /* The attacking player gets a card */
341 fGetsCard = TRUE;
342
343 /* Send a verbose message */
344 #ifdef ENGLISH
345 snprintf(buf, sizeof(buf), "%s captured %s from %s",
346 #endif
347 #ifdef FRENCH
348 snprintf(buf, sizeof(buf), "%s capture %s � partir de %s",
349 #endif
350 RISK_GetNameOfPlayer(iCurrentPlayer),
351 RISK_GetNameOfCountry(iDstCountry),
352 RISK_GetNameOfCountry(iSrcCountry));
353 mess.strMessage = buf;
354 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
355 MSG_NETMESSAGE, &mess);
356
357 /* Display informative message telling of victory */
358 #ifdef ENGLISH
359 snprintf(buf, sizeof(buf), "%s moving armies from %s to %s.",
360 RISK_GetNameOfPlayer(iCurrentPlayer),
361 RISK_GetNameOfCountry(iSrcCountry),
362 RISK_GetNameOfCountry(iDstCountry));
363 #endif
364 #ifdef FRENCH
365 snprintf(buf, sizeof(buf), "%s conqui�re %s � partir de %s.",
366 RISK_GetNameOfPlayer(iCurrentPlayer),
367 RISK_GetNameOfCountry(iDstCountry),
368 RISK_GetNameOfCountry(iSrcCountry));
369 #endif
370 UTIL_DisplayComment(buf);
371
372 /* Update data structures */
373 iSrc = RISK_GetOwnerOfCountry(iSrcCountry);
374 RISK_SetNumCountriesOfPlayer(iSrc, RISK_GetNumCountriesOfPlayer(iSrc)+1);
375 iDst = RISK_GetOwnerOfCountry(iDstCountry);
376 RISK_SetNumCountriesOfPlayer(iDst, RISK_GetNumCountriesOfPlayer(iDst)-1);
377
378 /* New owner */
379 RISK_SetOwnerOfCountry(iDstCountry, RISK_GetOwnerOfCountry(iSrcCountry));
380
381 /* See if this victory signalled a _MSG_ENDOFMISSION condition */
382 if (GAME_IsMissionAccomplied(iCurrentPlayer, iDst))
383 {
384 MsgEndOfMission msg;
385
386 msg.iWinner = iCurrentPlayer;
387 msg.iTyp = RISK_GetMissionTypeOfPlayer(iCurrentPlayer);
388 msg.iNum1 = RISK_GetMissionContinent1OfPlayer(iCurrentPlayer);
389 msg.iNum2 = RISK_GetMissionContinent2OfPlayer(iCurrentPlayer);
390 RISK_SetMissionTypeOfPlayer(iCurrentPlayer, NO_MISSION);
391 (void)RISK_SendSyncMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
392 MSG_ENDOFMISSION, &msg,
393 MSG_ENDOFMISSION, CBK_IncomingMessage);
394 /* If the winner stop the game ... */
395 if (iState == STATE_REGISTER)
396 return;
397 }
398
399 /* Move the desired number of armies if it isn't end of game */
400 if (RISK_GetNumCountriesOfPlayer(iDst) != 0 ||
401 RISK_GetNumLivePlayers() != 2)
402 {
403 iArmiesMove = UTIL_GetArmyNumber(iAttackDie,
404 RISK_GetNumArmiesOfCountry(iSrcCountry)-1,
405 FALSE);
406 RISK_SetNumArmiesOfCountry(iSrcCountry,
407 RISK_GetNumArmiesOfCountry(iSrcCountry) -
408 iArmiesMove);
409 RISK_SetNumArmiesOfCountry(iDstCountry,
410 RISK_GetNumArmiesOfCountry(iDstCountry) +
411 iArmiesMove);
412 }
413
414 /* See if this victory signalled a _DEADPLAYER or _ENDOFGAME condition */
415 if (RISK_GetNumCountriesOfPlayer(iDst) == 0)
416 GAME_PlayerDied(iDst);
417 }
418 else
419 {
420 /* If a new attack and not do-or-die, notify the server */
421 if (!(iCachedSrcCountry == iSrcCountry &&
422 iCachedDstCountry == iDstCountry))
423 {
424 /* Send a verbose message */
425 #ifdef ENGLISH
426 snprintf(buf, sizeof(buf), "%s is attacking from %s to %s",
427 RISK_GetNameOfPlayer(iCurrentPlayer),
428 RISK_GetNameOfCountry(iSrcCountry),
429 RISK_GetNameOfCountry(iDstCountry));
430 #endif
431 #ifdef FRENCH
432 snprintf(buf, sizeof(buf), "%s attaque %s � partir de %s",
433 RISK_GetNameOfPlayer(iCurrentPlayer),
434 RISK_GetNameOfCountry(iDstCountry),
435 RISK_GetNameOfCountry(iSrcCountry));
436 #endif
437 mess.strMessage = buf;
438 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
439 MSG_NETMESSAGE, &mess);
440 UTIL_DisplayComment(buf);
441 }
442 }
443
444 /* If we are told to notify, then notify */
445 if (fNotify == TRUE)
446 {
447 msgAttackNotify.iSrcCountry = iSrcCountry;
448 msgAttackNotify.iDstCountry = iDstCountry;
449 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
450 MSG_ATTACKNOTIFY, &msgAttackNotify);
451 }
452
453 /* Update cached values */
454 iCachedSrcCountry = iSrcCountry;
455 iCachedDstCountry = iDstCountry;
456 }
457
458
459 /************************************************************************
460 * FUNCTION: GAME_IsEnemyAdjacent
461 * HISTORY:
462 * 03.05.94 ESF Created.
463 * PURPOSE:
464 * NOTES:
465 ************************************************************************/
466 Flag GAME_IsEnemyAdjacent(Int32 iCountry)
467 {
468 Int32 i, iPlayer;
469
470 iPlayer = RISK_GetOwnerOfCountry(iCountry);
471 for (i=0; i!=6 && RISK_GetAdjCountryOfCountry(iCountry, i)!=-1; i++)
472 if (RISK_GetOwnerOfCountry(RISK_GetAdjCountryOfCountry(iCountry, i)) !=
473 iPlayer)
474 return TRUE;
475
476 return FALSE;
477 }
478
479
480 /************************************************************************
481 * FUNCTION: GAME_IsFrontierAdjacent
482 * HISTORY:
483 * 03.08.95 JC Created.
484 * PURPOSE:
485 * NOTES:
486 ************************************************************************/
487 Flag GAME_IsFrontierAdjacent(Int32 srcCountry, Int32 destCountry)
488 {
489 Int32 iPlayer, iContinent, iCountry, i;
490
491 iPlayer = RISK_GetOwnerOfCountry(srcCountry);
492 iContinent = RISK_GetContinentOfCountry(srcCountry);
493 if (RISK_GetContinentOfCountry(destCountry) != iContinent)
494 return GAME_IsEnemyAdjacent(destCountry);
495 for (i=0; i!=6 && RISK_GetAdjCountryOfCountry(destCountry, i)!=-1; i++)
496 {
497 iCountry = RISK_GetAdjCountryOfCountry(destCountry, i);
498 if (RISK_GetContinentOfCountry(iCountry) != iContinent)
499 return TRUE;
500 }
501
502 return FALSE;
503 }
504
505
506 /************************************************************************
507 * FUNCTION: FindEnemyAdjacent
508 * HISTORY:
509 * 11.08.95 JC Created.
510 * PURPOSE:
511 * NOTES:
512 ************************************************************************/
513 Int32 FindEnemyAdjacent(Int32 iCountry, Int32 distance)
514 {
515 Int32 i, min, res, iPlayer, dest;
516
517 iPlayer = RISK_GetOwnerOfCountry(iCountry);
518 min = 100000;
519 for (i=0; i!=6 && RISK_GetAdjCountryOfCountry(iCountry, i)!=-1; i++)
520 {
521 dest = RISK_GetAdjCountryOfCountry(iCountry, i);
522 if (RISK_GetOwnerOfCountry(dest) == iPlayer)
523 {
524 if (distance <= 3)
525 {
526 res = FindEnemyAdjacent(dest, distance + 1);
527 if (res < min)
528 min = res;
529 }
530 }
531 else
532 min = 0;
533 }
534
535 return (min + 1);
536 }
537
538
539 /************************************************************************
540 * FUNCTION: GAME_FindEnemyAdjacent
541 * HISTORY:
542 * 11.08.95 JC Created.
543 * PURPOSE:
544 * NOTES:
545 ************************************************************************/
546 Int32 GAME_FindEnemyAdjacent(Int32 iCountry)
547 {
548 Int32 i, min, res, iPlayer, dest, destCountry;
549
550 iPlayer = RISK_GetOwnerOfCountry(iCountry);
551 destCountry = -1;
552 min = 100000;
553 for (i=0; i!=6 && RISK_GetAdjCountryOfCountry(iCountry, i)!=-1; i++)
554 {
555 dest = RISK_GetAdjCountryOfCountry(iCountry, i);
556 if (RISK_GetOwnerOfCountry(dest) == iPlayer)
557 {
558 res = FindEnemyAdjacent(dest, 0);
559 if (res < min)
560 {
561 min = res;
562 destCountry = dest;
563 }
564 }
565 else
566 min = 0;
567 }
568
569 return (destCountry);
570 }
571
572
573 /************************************************************************
574 * FUNCTION: GAME_AttackClick
575 * HISTORY:
576 * 03.06.94 ESF Created.
577 * 04.02.94 ESF Added notification of server after Do-or-Die.
578 * 05.19.94 ESF Added verbose message across network.
579 * 05.19.94 ESF Fixed for fixed attack dice do-or-die.
580 * 07.14.94 ESF Fixed bug, bot checking attack dice on dest country.
581 * 10.15.94 ESF Added printing of "Attacking from foo to bar" locally.
582 * PURPOSE:
583 * NOTES:
584 ************************************************************************/
585 void GAME_AttackClick(Int32 iCountry)
586 {
587 char buf[256];
588 if (iAttackSrc < 0)
589 {
590 if (GAME_ValidAttackSrc(iCountry, TRUE) &&
591 GAME_ValidAttackDice(RISK_GetDiceModeOfPlayer(iCurrentPlayer),
592 iCountry))
593 {
594 iAttackSrc = iCountry;
595
596 #ifdef ENGLISH
597 snprintf(buf, sizeof(buf), "%s is attacking from %s to"
598 " <click on territory>",
599 #endif
600 #ifdef FRENCH
601 snprintf(buf, sizeof(buf), "%s attaque � partir de %s ",
602 #endif
603 RISK_GetNameOfPlayer(iCurrentPlayer),
604 RISK_GetNameOfCountry(iAttackSrc));
605 UTIL_DisplayComment(buf);
606 UTIL_LightCountry(iAttackSrc);
607 }
608 }
609 else if (GAME_ValidAttackDst(iAttackSrc, iCountry, TRUE) &&
610 GAME_ValidAttackDice(RISK_GetDiceModeOfPlayer(iCurrentPlayer),
611 iAttackSrc))
612 {
613 iAttackDst = iCountry;
614 GAME_Attack(iAttackSrc, iAttackDst);
615
616 iAttackSrc = iAttackDst = -1;
617 UTIL_DisplayActionCString(iState, iCurrentPlayer);
618 }
619 }
620
621
622 /************************************************************************
623 * FUNCTION: GAME_ValidAttackDice
624 * HISTORY:
625 * 05.19.94 ESF Created.
626 * PURPOSE:
627 * NOTES:
628 ************************************************************************/
629 Flag GAME_ValidAttackDice(Int32 iAttackDice, Int32 iCountry)
630 {
631 char buf[256];
632 /* If the dice have been selected manually, check them */
633 if (iAttackDice != ATTACK_AUTO)
634 {
635 if (RISK_GetNumArmiesOfCountry(iCountry) <= iAttackDice+1)
636 {
637 #ifdef ENGLISH
638 snprintf(buf, sizeof(buf), "You can't attack with %d dice, dummy.",
639 #endif
640 #ifdef FRENCH
641 snprintf(buf, sizeof(buf), "Attaque impossible avec %d d�(s).",
642 #endif
643 iAttackDice+1);
644 UTIL_DarkenCountry(iCountry);
645 UTIL_DisplayError(buf);
646 return FALSE;
647 }
648 }
649 return TRUE;
650 }
651
652
653 /************************************************************************
654 * FUNCTION: GAME_ValidAttackSrc
655 * HISTORY:
656 * 03.06.94 ESF Created.
657 * 03.07.94 ESF Added verbose option
658 * PURPOSE:
659 * NOTES:
660 ************************************************************************/
661 Flag GAME_ValidAttackSrc(Int32 iAttackSrc, Flag fVerbose)
662 {
663 char buf[256];
664 if (iAttackSrc < 0)
665 {
666 if (fVerbose)
667 #ifdef ENGLISH
668 UTIL_DisplayError("Where the hell are you trying to attack from?");
669 #endif
670 #ifdef FRENCH
671 UTIL_DisplayError("D'o� voulez-vous attaquer?");
672 #endif
673 }
674 else if (iAttackSrc >= NUM_COUNTRIES)
675 {
676 if (fVerbose)
677 #ifdef ENGLISH
678 UTIL_DisplayError("You can't attack from the ocean!");
679 #endif
680 #ifdef FRENCH
681 UTIL_DisplayError("Attaque impossible � partir de l'oc�an!");
682 #endif
683 }
684 else if (RISK_GetOwnerOfCountry(iAttackSrc) != iCurrentPlayer)
685 {
686 if (fVerbose)
687 {
688 #ifdef ENGLISH
689 snprintf(buf, sizeof(buf), "You can't attack from %s -- you don't own it.",
690 #endif
691 #ifdef FRENCH
692 snprintf(buf, sizeof(buf), "Attaque impossible � partir de %s.",
693 #endif
694 RISK_GetNameOfCountry(iAttackSrc));
695 UTIL_DisplayError(buf);
696 }
697 }
698 else if (!GAME_IsEnemyAdjacent(iAttackSrc))
699 {
700 if (fVerbose)
701 {
702 #ifdef ENGLISH
703 snprintf(buf, sizeof(buf), "There are no enemy territories you can attack from %s.",
704 #endif
705 #ifdef FRENCH
706 snprintf(buf, sizeof(buf), "Pas de territoire � attaquer � partir de %s.",
707 #endif
708 RISK_GetNameOfCountry(iAttackSrc));
709 UTIL_DisplayError(buf);
710 }
711 }
712 else if (RISK_GetNumArmiesOfCountry(iAttackSrc) == 1)
713 {
714 if (fVerbose)
715 #ifdef ENGLISH
716 UTIL_DisplayError("You can't attack with 1 army.");
717 #endif
718 #ifdef FRENCH
719 UTIL_DisplayError("Attaque impossible avec une seule arm�e.");
720 #endif
721 }
722 else
723 return TRUE;
724
725 return FALSE;
726 }
727
728
729 /************************************************************************
730 * FUNCTION: GAME_ValidAttackDst
731 * HISTORY:
732 * 03.06.94 ESF Created.
733 * 03.07.94 ESF Added verbose option
734 * PURPOSE:
735 * NOTES:
736 ************************************************************************/
737 Flag GAME_ValidAttackDst(Int32 iAttackSrc, Int32 iAttackDst, Flag fVerbose)
738 {
739 char buf[256];
740 if (iAttackDst < 0)
741 {
742 if (fVerbose)
743 #ifdef ENGLISH
744 UTIL_DisplayError("Where the hell are you trying to attack from?");
745 #endif
746 #ifdef FRENCH
747 UTIL_DisplayError("O� voulez-vous attaquer?");
748 #endif
749 }
750 else if (iAttackDst >= NUM_COUNTRIES)
751 {
752 if (fVerbose)
753 #ifdef ENGLISH
754 UTIL_DisplayError("You can't attack the ocean!");
755 #endif
756 #ifdef FRENCH
757 UTIL_DisplayError("Impossible d'attaquer l'oc�an!");
758 #endif
759 }
760 else
761 {
762 if (!GAME_CanAttack(iAttackSrc, iAttackDst))
763 {
764 if (fVerbose)
765 {
766 #ifdef ENGLISH
767 snprintf(buf, sizeof(buf), "You can't attack %s from %s!",
768 #endif
769 #ifdef FRENCH
770 snprintf(buf, sizeof(buf), "Attaque impossible de %s � partir de %s!",
771 #endif
772 RISK_GetNameOfCountry(iAttackDst),
773 RISK_GetNameOfCountry(iAttackSrc));
774 UTIL_DisplayError(buf);
775 }
776 }
777 else if (RISK_GetOwnerOfCountry(iAttackDst) == iCurrentPlayer)
778 {
779 if (fVerbose)
780 {
781 #ifdef ENGLISH
782 snprintf(buf, sizeof(buf), "You can't attack %s -- you own it.",
783 #endif
784 #ifdef FRENCH
785 snprintf(buf, sizeof(buf), "Attaque impossible de son propre territoire (%s).",
786 #endif
787 RISK_GetNameOfCountry(iAttackDst));
788 UTIL_DisplayError(buf);
789 }
790 }
791 else
792 return TRUE;
793 }
794
795 return FALSE;
796 }
797
798
799 /************************************************************************
800 * FUNCTION: GAME_PlaceClick
801 * HISTORY:
802 * 03.07.94 ESF Created.
803 * 03.16.94 ESF Added support for placing multiple armies.
804 * 05.19.94 ESF Factored out code to GAME_PlaceArmies.
805 * 04.30.95 ESF Brought the dialog code from PlaceArmies here.
806 * PURPOSE:
807 * NOTES:
808 ************************************************************************/
809 void GAME_PlaceClick(Int32 iCountry, Int32 iPlaceType)
810 {
811 Int32 maxN;
812 /* Is the country picked the player's? */
813 if (GAME_ValidPlaceDst(iCountry)) {
814 Int32 iArmies;
815
816 maxN = RISK_GetNumArmiesOfPlayer(iCurrentPlayer);
817 #ifndef DROPALL
818 if ( iState == STATE_FORTIFY )
819 maxN = (maxN > DROP_ARMIES) ? DROP_ARMIES : maxN;
820 #endif
821
822 /* Depending on the type of placement, popup a dialog or not */
823 if (iPlaceType == PLACE_MULTIPLE)
824 {
825 iArmies = UTIL_GetArmyNumber(1, maxN, TRUE);
826 if (!iArmies)
827 return;
828 }
829 else if (iPlaceType == PLACE_ALL)
830 iArmies = maxN;
831 else
832 iArmies = 1;
833
834 GAME_PlaceArmies(iCountry, iArmies);
835
836 /* If we're fortifying, we're done. */
837 if (iState == STATE_FORTIFY)
838 GAME_EndTurn();
839 }
840 }
841
842
843 /************************************************************************
844 * FUNCTION: GAME_ValidPlaceDst
845 * HISTORY:
846 * 03.07.94 ESF Created.
847 * PURPOSE:
848 * NOTES:
849 ************************************************************************/
850 Flag GAME_ValidPlaceDst(Int32 iPlaceDst)
851 {
852 if (iPlaceDst < 0)
853 #ifdef ENGLISH
854 UTIL_DisplayError("What the hell country is that!");
855 #endif
856 #ifdef FRENCH
857 UTIL_DisplayError("What the hell country is that!");
858 #endif
859 else if (iPlaceDst >= NUM_COUNTRIES)
860 #ifdef ENGLISH
861 UTIL_DisplayError("Ahhh...That's the ocean, buddy...");
862 #endif
863 #ifdef FRENCH
864 UTIL_DisplayError("Ahhh...C'est l'oc�an, plouf...");
865 #endif
866 else if (RISK_GetOwnerOfCountry(iPlaceDst) != iCurrentPlayer)
867 #ifdef ENGLISH
868 UTIL_DisplayError("You cannot place armies on opponent's territories.");
869 #endif
870 #ifdef FRENCH
871 UTIL_DisplayError("Donner des arm�es � un autre joueur?");
872 #endif
873 else
874 return TRUE;
875
876 return FALSE;
877 }
878
879
880 /************************************************************************
881 * FUNCTION: GAME_MoveClick
882 * HISTORY:
883 * 03.07.94 ESF Created.
884 * 03.29.94 ESF Added a message when iMoveDst is verified.
885 * 03.29.94 ESF Fixed lack of UTIL_DisplayComment() bug.
886 * 01.09.95 JC End turn only if iState asn't moved to STATE_REGISTER.
887 * PURPOSE:
888 * NOTES:
889 ************************************************************************/
890 void GAME_MoveClick(Int32 iCountry)
891 {
892 Int32 iNumArmies;
893 char buf[256];
894
895 if (iMoveSrc < 0)
896 {
897 if (GAME_ValidMoveSrc(iCountry))
898 {
899 iMoveSrc = iCountry;
900
901 #ifdef ENGLISH
902 snprintf(buf, sizeof(buf), "%s moving armies from %s to"
903 " <click on territory>",
904 #endif
905 #ifdef FRENCH
906 snprintf(buf, sizeof(buf), "%s d�place des arm�es de %s en"
907 " <click on territory>",
908 #endif
909 RISK_GetNameOfPlayer(iCurrentPlayer),
910 RISK_GetNameOfCountry(iMoveSrc));
911 UTIL_DisplayComment(buf);
912 UTIL_LightCountry(iMoveSrc);
913 }
914 }
915 else if (GAME_ValidMoveDst(iMoveSrc, iCountry))
916 {
917 iMoveDst = iCountry;
918
919 #ifdef ENGLISH
920 snprintf(buf, sizeof(buf), "%s moving armies from %s to %s.",
921 #endif
922 #ifdef FRENCH
923 snprintf(buf, sizeof(buf), "%s d�place des arm�es de %s en %s.",
924 #endif
925 RISK_GetNameOfPlayer(iCurrentPlayer),
926 RISK_GetNameOfCountry(iMoveSrc),
927 RISK_GetNameOfCountry(iMoveDst));
928 UTIL_DisplayComment(buf);
929
930 iNumArmies = UTIL_GetArmyNumber(1,
931 RISK_GetNumArmiesOfCountry(iMoveSrc)-1,
932 TRUE);
933 if (iNumArmies>0)
934 {
935 GAME_MoveArmies(iMoveSrc, iMoveDst, iNumArmies);
936
937 if (iState != STATE_REGISTER)
938 /* Turn over, man! */
939 GAME_EndTurn();
940 }
941 else
942 {
943 UTIL_DarkenCountry(iMoveSrc);
944 iMoveSrc = iMoveDst = -1;
945 }
946 }
947 }
948
949
950 /************************************************************************
951 * FUNCTION: GAME_ValidMoveSrc
952 * HISTORY:
953 * 03.07.94 ESF Created.
954 * 03.29.94 ESF fixed a small bug, inserted "else".
955 * PURPOSE:
956 * NOTES:
957 ************************************************************************/
958 Flag GAME_ValidMoveSrc(Int32 iMoveSrc)
959 {
960 if (iMoveSrc < 0)
961 #ifdef ENGLISH
962 UTIL_DisplayError("Where the hell are you trying to move from?");
963 #endif
964 #ifdef FRENCH
965 UTIL_DisplayError("Where the hell are you trying to move from?");
966 #endif
967 else if (iMoveSrc >= NUM_COUNTRIES)
968 #ifdef ENGLISH
969 UTIL_DisplayError("You can't move armies from the ocean.");
970 #endif
971 #ifdef FRENCH
972 UTIL_DisplayError("Impossible de d�placer des arm�es � partir de l'oc�an.");
973 #endif
974 else if (RISK_GetOwnerOfCountry(iMoveSrc) != iCurrentPlayer)
975 #ifdef ENGLISH
976 UTIL_DisplayError("You cannot move armies from an opponent's"
977 " territories.");
978 #endif
979 #ifdef FRENCH
980 UTIL_DisplayError("Impossible de prendre les arm�es d'un autre joueur.");
981 #endif
982 else if (RISK_GetNumArmiesOfCountry(iMoveSrc) == 1)
983 #ifdef ENGLISH
984 UTIL_DisplayError("You cannot move from territories that"
985 " have one army to begin with.");
986 #endif
987 #ifdef FRENCH
988 UTIL_DisplayError("Impossible de d�placer la seule arm�e d'un territoire.");
989 #endif
990 else
991 return TRUE;
992
993 return FALSE;
994 }
995
996
997 /************************************************************************
998 * FUNCTION: GAME_ValidMoveDst
999 * HISTORY:
1000 * 03.07.94 ESF Created.
1001 * PURPOSE:
1002 * NOTES: used to get iMoveDst passed, which is a global static in game.c
1003 ************************************************************************/
1004 Flag GAME_ValidMoveDst(Int32 iMoveSrc, Int32 iMoveDst)
1005 {
1006 char buf[256];
1007 if (iMoveDst < 0)
1008 #ifdef ENGLISH
1009 UTIL_DisplayError("Where the hell are you trying to move from?");
1010 #endif
1011 #ifdef FRENCH
1012 UTIL_DisplayError("Where the hell are you trying to move from?");
1013 #endif
1014 else if (iMoveDst >= NUM_COUNTRIES)
1015 #ifdef ENGLISH
1016 UTIL_DisplayError("You can't move armies into the ocean -- they'd drown.");
1017 #endif
1018 #ifdef FRENCH
1019 UTIL_DisplayError("Impossible de placer des arm�es dans l'oc�an.");
1020 #endif
1021 else if (RISK_GetOwnerOfCountry(iMoveDst) != iCurrentPlayer)
1022 #ifdef ENGLISH
1023 UTIL_DisplayError("You cannot move armies to an opponent's"
1024 " territories.");
1025 #endif
1026 #ifdef FRENCH
1027 UTIL_DisplayError("Impossible de placer des arm�es sur un territoire ennemi.");
1028 #endif
1029 else if (!GAME_CanAttack(iMoveSrc, iMoveDst))
1030 {
1031 #ifdef ENGLISH
1032 snprintf(buf, sizeof(buf), "Armies can't get to %s from %s",
1033 #endif
1034 #ifdef FRENCH
1035 snprintf(buf, sizeof(buf), "D�placement impossible de %s en %s",
1036 #endif
1037 RISK_GetNameOfCountry(iMoveDst),
1038 RISK_GetNameOfCountry(iMoveSrc));
1039 UTIL_DisplayError(buf);
1040 }
1041 else
1042 return TRUE;
1043
1044 return FALSE;
1045 }
1046
1047
1048 /************************************************************************
1049 * FUNCTION: GAME_GetContinentBonus
1050 * HISTORY:
1051 * 03.16.94 ESF Created.
1052 * 01.09.95 ESF Changed an if..else to an assertion.
1053 * PURPOSE:
1054 * NOTES:
1055 ************************************************************************/
1056 Int32 GAME_GetContinentBonus(Int32 iPlayer)
1057 {
1058 Int32 piCount[NUM_CONTINENTS];
1059 Int32 i, iArmies=0;
1060
1061 /* Init. */
1062 for (i=0; i!=NUM_CONTINENTS; i++)
1063 piCount[i] = 0;
1064
1065 /* Count up how many countries the player has in each of the continents */
1066 for (i=0; i!=NUM_COUNTRIES; i++)
1067 if (RISK_GetOwnerOfCountry(i) == iPlayer)
1068 {
1069 /* Sanity check */
1070 D_Assert(RISK_GetContinentOfCountry(i) >= 0 &&
1071 RISK_GetContinentOfCountry(i) < NUM_CONTINENTS,
1072 "Country has messed up number of armies.");
1073
1074 piCount[RISK_GetContinentOfCountry(i)]++;
1075 }
1076
1077 /* If the player has the total number of countries, give him or her bonus */
1078 for (i=0; i!=NUM_CONTINENTS; i++)
1079 if (RISK_GetNumCountriesOfContinent(i) == piCount[i])
1080 iArmies += RISK_GetValueOfContinent(i);
1081
1082 return(iArmies);
1083 }
1084
1085
1086 /************************************************************************
1087 * FUNCTION: GAME_PlayerDied
1088 * HISTORY:
1089 * 03.28.94 ESF Created.
1090 * 03.29.94 ESF Fixed off-by-one bug in end of game detection.
1091 * 04.11.94 ESF Added iKillerPlayer parameter.
1092 * 05.04.94 ESF Fixed bugs and exhanced.
1093 * 05.12.94 ESF Fixed bug, reset dead player's cards to 0.
1094 * 09.31.94 ESF Changed because of new ENDOFGAME contents.
1095 * 10.02.94 ESF Changed to set number of live players == 0.
1096 * 10.30.94 ESF Fixed bug, shouldn't be setting iNumLivePlayers to 0.
1097 * 01.09.95 ESF Changed to be more verbose.
1098 * 30.08.95 JC Talk the server: "force exchange of cards?".
1099 * PURPOSE:
1100 * NOTES:
1101 ************************************************************************/
1102 void GAME_PlayerDied(Int32 iDeadPlayer)
1103 {
1104 MsgMessagePacket msg;
1105 char buf[256];
1106
1107 /* Notify everybody */
1108 #ifdef ENGLISH
1109 snprintf(buf, sizeof(buf), "%s was destroyed by %s (who gained %d card%s).",
1110 #endif
1111 #ifdef FRENCH
1112 snprintf(buf, sizeof(buf), "%s est d�truit par %s (qui gagne %d carte%s).",
1113 #endif
1114 RISK_GetNameOfPlayer(iDeadPlayer),
1115 RISK_GetNameOfPlayer(iCurrentPlayer),
1116 RISK_GetNumCardsOfPlayer(iDeadPlayer),
1117 (RISK_GetNumCardsOfPlayer(iDeadPlayer)>1)?"s":"");
1118 msg.strMessage = buf;
1119 msg.iFrom = FROM_SERVER;
1120 msg.iTo = DST_ALLPLAYERS;
1121 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1122 MSG_MESSAGEPACKET, &msg);
1123
1124 /* Kill the player */
1125 RISK_SetStateOfPlayer(iDeadPlayer, PLAYER_DEAD);
1126
1127 /* Is there only one player left? If so, end of game... */
1128 if (RISK_GetNumLivePlayers() == 1)
1129 {
1130 MsgVictory msgVictory;
1131
1132 msgVictory.iWinner = iCurrentPlayer;
1133 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1134 MSG_VICTORY, &msgVictory);
1135 }
1136 else
1137 {
1138 MsgForceExchangeCards msg;
1139 Int32 i, n, m;
1140
1141 /* Give the killer player all of the cards */
1142 n = RISK_GetNumCardsOfPlayer(iCurrentPlayer);
1143 m = RISK_GetNumCardsOfPlayer(iDeadPlayer);
1144
1145 /* Let the player know how many cards he or she got. */
1146 if (m>0)
1147 {
1148 #ifdef ENGLISH
1149 snprintf(buf, sizeof(buf), "%s got %d card%s from %s.",
1150 #endif
1151 #ifdef FRENCH
1152 snprintf(buf, sizeof(buf), "%s prend %d carte%s de %s.",
1153 #endif
1154 RISK_GetNameOfPlayer(iCurrentPlayer),
1155 m, m==1?"":"s", RISK_GetNameOfPlayer(iDeadPlayer));
1156 mess.strMessage = buf;
1157 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1158 MSG_NETMESSAGE, &mess);
1159 }
1160
1161 for (i=0; i!=m; i++)
1162 RISK_SetCardOfPlayer(iCurrentPlayer, n+i,
1163 RISK_GetCardOfPlayer(iDeadPlayer, i));
1164 RISK_SetNumCardsOfPlayer(iCurrentPlayer, n+m);
1165 RISK_SetNumCardsOfPlayer(iDeadPlayer, 0);
1166
1167 if (m>0)
1168 {
1169 /* Force the player to exchange if he or she possesses
1170 * too many cards.
1171 */
1172 msg.iPlayer = iCurrentPlayer;
1173 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1174 MSG_FORCEEXCHANGECARDS, &msg);
1175 }
1176 }
1177 }
1178
1179
1180 /************************************************************************
1181 * FUNCTION: GAME_MissionToStr
1182 * HISTORY:
1183 * 29.08.95 JC Created (copy of GAME_ShowMission).
1184 * PURPOSE: Put in strScratch a description of the mission
1185 * NOTES:
1186 ************************************************************************/
1187 void GAME_MissionToStr(Int32 iPlayer, Int16 iTyp, Int32 iNum1, Int32 iNum2, Flag fVict, char *buf, size_t bufsize)
1188 {
1189 UNUSED(iPlayer);
1190
1191 #ifdef FRENCH
1192 switch (iTyp)
1193 {
1194 case NO_MISSION:
1195 snprintf(buf, bufsize, "Plus de mission -> Conquerir le monde");
1196 break;
1197 case CONQUIER_WORLD:
1198 if (fVict)
1199 snprintf(buf, bufsize, "Mission accomplie: le monde est conquit");
1200 else
1201 snprintf(buf, bufsize, "Mission: Conquerir le monde");
1202 break;
1203 case CONQUIER_Nb_COUNTRY:
1204 if (fVict)
1205 snprintf(buf, bufsize, "Mission accomplie: %d territoires conquis",
1206 iNum1);
1207 else
1208 snprintf(buf, bufsize, "Mission: Conquerir %d territoires",
1209 iNum1);
1210 break;
1211 case CONQUIER_TWO_CONTINENTS:
1212 if (fVict)
1213 snprintf(buf, bufsize, "Mission accomplie: Conquerir %s et %s",
1214 RISK_GetNameOfContinent(iNum1),
1215 RISK_GetNameOfContinent(iNum2));
1216 else
1217 snprintf(buf, bufsize, "Mission: Conquerir %s et %s",
1218 RISK_GetNameOfContinent(iNum1),
1219 RISK_GetNameOfContinent(iNum2));
1220 break;
1221 case KILL_A_PLAYER:
1222 if (fVict || iNum2)
1223 snprintf(buf, bufsize, "Mission accomplie: %s est mort",
1224 RISK_GetNameOfPlayer(iNum1));
1225 else if (RISK_GetNumCountriesOfPlayer(iNum1) <= 0)
1226 snprintf(buf, bufsize, "Mission impossible: Tuer %s qui est d�j� mort",
1227 RISK_GetNameOfPlayer(iNum1));
1228 else
1229 snprintf(buf, bufsize, "Mission: Tuer %s",
1230 RISK_GetNameOfPlayer(iNum1));
1231 break;
1232 }
1233 #endif
1234 #ifdef ENGLISH
1235 switch (iTyp)
1236 {
1237 case NO_MISSION:
1238 snprintf(buf, bufsize, "Additional mission -> Conquer the world");
1239 break;
1240 case CONQUIER_WORLD:
1241 if (fVict)
1242 snprintf(buf, bufsize, "Mission accomplished: the world is conquered");
1243 else
1244 snprintf(buf, bufsize, "Mission: Conquer the world");
1245 break;
1246 case CONQUIER_Nb_COUNTRY:
1247 if (fVict)
1248 snprintf(buf, bufsize, "Mission accomplished: %d countries conquered",
1249 iNum1);
1250 else
1251 snprintf(buf, bufsize, "Mission: Conquer %d countries",
1252 iNum1);
1253 break;
1254 case CONQUIER_TWO_CONTINENTS:
1255 if (fVict)
1256 snprintf(buf, bufsize, "Mission accomplished: Conquer %s and %s",
1257 RISK_GetNameOfContinent(iNum1),
1258 RISK_GetNameOfContinent(iNum2));
1259 else
1260 snprintf(buf, bufsize, "Mission: Conquer %s and %s",
1261 RISK_GetNameOfContinent(iNum1),
1262 RISK_GetNameOfContinent(iNum2));
1263 break;
1264 case KILL_A_PLAYER:
1265 if (fVict || iNum2)
1266 snprintf(buf, bufsize, "Mission accomplished: %s is dead",
1267 RISK_GetNameOfPlayer(iNum1));
1268 else if (RISK_GetNumCountriesOfPlayer(iNum1) <= 0)
1269 snprintf(buf, bufsize, "Mission impossible: kill %s who's already dead",
1270 RISK_GetNameOfPlayer(iNum1));
1271 else
1272 snprintf(buf, bufsize, "Mission: Kill %s",
1273 RISK_GetNameOfPlayer(iNum1));
1274 break;
1275 }
1276 #endif
1277 }
1278
1279
1280 /************************************************************************
1281 * FUNCTION: GAME_MissionAccomplied
1282 * HISTORY:
1283 * 25.08.95 JC Created from different pieces.
1284 * PURPOSE:
1285 * NOTES:
1286 ************************************************************************/
1287 void GAME_MissionAccomplied(Int32 iWinner, Int16 iTyp, Int32 iNum1, Int32 iNum2)
1288 {
1289 Char str[256];
1290 Int32 iChoice;
1291 char buf[256];
1292
1293 GAME_MissionToStr(iWinner, iTyp, iNum1, iNum2, TRUE, buf, sizeof(buf));
1294 if (RISK_GetClientOfPlayer(iWinner) != CLNT_GetThisClientID())
1295 {
1296 /* Player that has won is non-local */
1297 #ifdef ENGLISH
1298 snprintf(str, sizeof(str),
1299 "%s has accomplied his mission!\n%s\n",
1300 RISK_GetNameOfPlayer(iCurrentPlayer),
1301 buf);
1302 (void)UTIL_PopupDialog("End of Mission", str, 2, "Cool!",
1303 "So What?", NULL);
1304 #endif
1305 #ifdef FRENCH
1306 snprintf(str, sizeof(str),
1307 "%s a termin� sa mission!\n%s\n",
1308 RISK_GetNameOfPlayer(iCurrentPlayer),
1309 buf);
1310 (void)UTIL_PopupDialog("Mission termin�e", str, 2, "Cool!",
1311 "Et alors?", NULL);
1312 #endif
1313 return;
1314 }
1315
1316 /* Winning player is local, congratulate him or her */
1317 #ifdef ENGLISH
1318 (void)UTIL_PopupDialog(RISK_GetNameOfPlayer(iCurrentPlayer),
1319 "You WON!!!", 2, "Cool!",
1320 "So What?", NULL);
1321 #endif
1322 #ifdef FRENCH
1323 (void)UTIL_PopupDialog(RISK_GetNameOfPlayer(iCurrentPlayer),
1324 "Victoire!!!", 2, "Cool!",
1325 "Et alors?", NULL);
1326 #endif
1327
1328 /* See if the player wants to continue or play again */
1329 #ifdef ENGLISH
1330 if (RISK_GetNumLivePlayers () > 1)
1331 iChoice = UTIL_PopupDialog("Game over", "Play again?",
1332 3, "Yes", "No", "Continue");
1333 else
1334 iChoice = UTIL_PopupDialog("Game over", "Play again?",
1335 2, "Yes", "No", NULL);
1336 #endif
1337 #ifdef FRENCH
1338 if (RISK_GetNumLivePlayers () > 1)
1339 iChoice = UTIL_PopupDialog("Partie termin�e", "Jouer une autre partie?",
1340 3, "Oui", "Non", "Continuer");
1341 else
1342 iChoice = UTIL_PopupDialog("Partie termin�e", "Jouer une autre partie?",
1343 2, "Oui", "Non", NULL);
1344 #endif
1345 if (iChoice == QUERY_NO)
1346 {
1347 /* Leave the game */
1348 UTIL_ExitProgram(0);
1349 }
1350 if (iChoice == QUERY_CANCEL)
1351 return;
1352
1353 iState = STATE_REGISTER;
1354 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1355 MSG_ENDOFGAME, NULL);
1356 }
1357
1358
1359 /************************************************************************
1360 * FUNCTION: GAME_Victory
1361 * HISTORY:
1362 * 28.08.95 JC Created from different pieces.
1363 * PURPOSE:
1364 * NOTES:
1365 ************************************************************************/
1366 void GAME_Victory(Int32 iWinner)
1367 {
1368 Int32 iChoice;
1369 char buf[256];
1370
1371 if (RISK_GetClientOfPlayer(iWinner) != CLNT_GetThisClientID())
1372 {
1373 /* Player that has won is non-local */
1374 #ifdef ENGLISH
1375 snprintf(buf, sizeof(buf), "%s has won!", RISK_GetNameOfPlayer(iCurrentPlayer));
1376 (void)UTIL_PopupDialog("End of Game", buf, 2, "Cool!",
1377 "So What?", NULL);
1378 #endif
1379 #ifdef FRENCH
1380 snprintf(buf, sizeof(buf), "%s a gagn�!", RISK_GetNameOfPlayer(iCurrentPlayer));
1381 (void)UTIL_PopupDialog("Fin du jeu", buf, 2, "Cool!",
1382 "Et alors?", NULL);
1383 #endif
1384 return;
1385 }
1386
1387 /* Winning player is local, congratulate him or her */
1388 #ifdef ENGLISH
1389 (void)UTIL_PopupDialog(RISK_GetNameOfPlayer(iCurrentPlayer),
1390 "You WON!!!", 2, "Cool!",
1391 "So What?", NULL);
1392 #endif
1393 #ifdef FRENCH
1394 (void)UTIL_PopupDialog(RISK_GetNameOfPlayer(iCurrentPlayer),
1395 "Victoire!!!", 2, "Cool!",
1396 "Et alors?", NULL);
1397 #endif
1398
1399 /* See if the player wants to continue or play again */
1400 #ifdef ENGLISH
1401 iChoice = UTIL_PopupDialog("Game over", "Play again?",
1402 2, "Yes", "No", NULL);
1403 #endif
1404 #ifdef FRENCH
1405 iChoice = UTIL_PopupDialog("Partie termin�e", "Jouer une autre partie?",
1406 2, "Oui", "Non", NULL);
1407 #endif
1408 if (iChoice == QUERY_NO)
1409 {
1410 /* Leave the game */
1411 UTIL_ExitProgram(0);
1412 }
1413
1414 iState = STATE_REGISTER;
1415 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1416 MSG_ENDOFGAME, NULL);
1417 }
1418
1419
1420 /************************************************************************
1421 * FUNCTION: GAME_GameOverMan
1422 * HISTORY:
1423 * 03.28.94 ESF Created.
1424 * 05.10.94 ESF Completed.
1425 * 05.12.94 ESF Added more functionality.
1426 * 01.05.95 DFE Fixed "Free Card" bug -- gave 1st person free card.
1427 * 02.13.95 ESF Updated to reflect new registration interface.
1428 * PURPOSE:
1429 * NOTES:
1430 ************************************************************************/
1431 void GAME_GameOverMan(void)
1432 {
1433 /* There will be another game */
1434 fGameStarted = FALSE;
1435
1436 /* Since EndTurn isn't called, need to reset this so that first player
1437 * doesn't get a free card next game.
1438 */
1439 fGetsCard = FALSE;
1440
1441 /* The state will be one of registering */
1442 if (iState != STATE_REGISTER)
1443 {
1444 iState = STATE_REGISTER;
1445 UTIL_ServerEnterState(iState);
1446 }
1447
1448 UTIL_DisplayError("");
1449 UTIL_DisplayComment("");
1450
1451 /* Set the colors to be of the initial setup */
1452 COLOR_SetWorldColors();
1453
1454 /* Erase the dice roll */
1455 DICE_Hide();
1456
1457 /* Get the new players */
1458 REG_PopupDialog();
1459 }
1460
1461
1462 /************************************************************************
1463 * FUNCTION: GAME_ExchangeCards
1464 * HISTORY:
1465 * 03.29.94 ESF Created.
1466 * 04.23.95 ESF Fixed (terrible) card take-away bug.
1467 * 05.13.95 ESF Fixed (horrible) country bonus bug -- thanks Alan!
1468 * 22.08.95 JC Fixed a bug with computerized player
1469 * (piCards not ordered).
1470 * PURPOSE:
1471 * NOTES:
1472 ************************************************************************/
1473 void GAME_ExchangeCards(Int32 *piCards)
1474 {
1475 Int32 i, j, k, piOldCards[3];
1476 MsgExchangeCards msgCards;
1477 MsgNetMessage msgNetMessage;
1478 char buf[256];
1479
1480 /* only 3 cards because we're handling selected cards */
1481 /* Save a copy and transform array indices into card indices */
1482 for (i=0; i!=3; i++)
1483 {
1484 piOldCards[i] = piCards[i];
1485 piCards[i] = RISK_GetCardOfPlayer(iCurrentPlayer, piCards[i]);
1486 }
1487
1488 /* Send the cards to the server -- this will generate an _REPLYPACKET */
1489 for (i=0; i!=3; i++)
1490 msgCards.piCards[i] = piCards[i];
1491 (void)RISK_SendSyncMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1492 MSG_EXCHANGECARDS, &msgCards,
1493 MSG_REPLYPACKET, CBK_IncomingMessage);
1494
1495 /* Update the date structures (bonus + countries on cards) */
1496 RISK_SetNumArmiesOfPlayer(iCurrentPlayer,
1497 RISK_GetNumArmiesOfPlayer(iCurrentPlayer)+iReply);
1498
1499 /* See if any of the cards match countries that the player has */
1500 for (i=0; i!=3; i++)
1501 if (piCards[i] < NUM_COUNTRIES && /* Not a joker! */
1502 RISK_GetOwnerOfCountry(piCards[i]) == iCurrentPlayer)
1503 RISK_SetNumArmiesOfCountry(piCards[i],
1504 RISK_GetNumArmiesOfCountry(piCards[i])+2);
1505
1506 /* Take the cards away from the player */
1507 for (i=0; i!=3; i++)
1508 {
1509 for (j=piOldCards[i]; j<MAX_CARDS-1; j++)
1510 RISK_SetCardOfPlayer(iCurrentPlayer, j,
1511 RISK_GetCardOfPlayer(iCurrentPlayer, j+1));
1512
1513 /* Now that we've moved all the cards down, we have to move the
1514 * pointers to the selected cards down as well. This was a bug.
1515 */
1516
1517 for (k=i+1; k<3; k++)
1518 if (piOldCards[k]>piOldCards[i])
1519 piOldCards[k] = piOldCards[k]-1;
1520 }
1521
1522 RISK_SetNumCardsOfPlayer(iCurrentPlayer,
1523 RISK_GetNumCardsOfPlayer(iCurrentPlayer)-3);
1524
1525 /* Display a message */
1526 #ifdef ENGLISH
1527 snprintf(buf, sizeof(buf), "%s got %d armies from exchanging cards.",
1528 #endif
1529 #ifdef FRENCH
1530 snprintf(buf, sizeof(buf), "%s gagne %d arm�es en �changeant des cartes.",
1531 #endif
1532 RISK_GetNameOfPlayer(iCurrentPlayer),
1533 iReply);
1534 UTIL_DisplayError(buf);
1535 msgNetMessage.strMessage = buf;
1536 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()), MSG_NETMESSAGE,
1537 &msgNetMessage);
1538 }
1539
1540
1541 /************************************************************************
1542 * FUNCTION: GAME_EndTurn
1543 * HISTORY:
1544 * 03.17.94 ESF Created.
1545 * 03.28.94 ESF Added card code, do we need Sync on the call?
1546 * 03.28.94 ESF Moved iAttack/iDefend stuff here.
1547 * 03.29.94 ESF Added iMove stuff.
1548 * 04.11.94 ESF Added clear dice call.
1549 * 04.11.94 ESF Move message call to end.
1550 * 02.20.95 ESF Moved to game.c from utils.c
1551 * PURPOSE:
1552 * NOTES:
1553 ************************************************************************/
1554 void GAME_EndTurn(void)
1555 {
1556 /* Get the player a card if he or she needs one */
1557 if (fGetsCard)
1558 {
1559 MsgRequestCard msg;
1560
1561 fGetsCard = FALSE;
1562 msg.iPlayer = iCurrentPlayer;
1563 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1564 MSG_REQUESTCARD, &msg);
1565 }
1566
1567 /* Darken the country if the player was attacking or moving */
1568 if (iAttackSrc >= 0)
1569 UTIL_DarkenCountry(iAttackSrc);
1570 if (iMoveSrc >= 0)
1571 UTIL_DarkenCountry(iMoveSrc);
1572
1573 /* Clear the dice box */
1574 DICE_Hide();
1575
1576 /* Init. variables for next turn */
1577 fCanExchange = TRUE;
1578 iAttackSrc = iAttackDst = iLastAttackSrc = iLastAttackDst = -1;
1579 iMoveSrc = iMoveDst = -1;
1580
1581 /* Notify the server of the end-of-turn condition and wait for a response */
1582 (void)RISK_SendSyncMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
1583 MSG_ENDTURN, NULL,
1584 MSG_TURNNOTIFY, CBK_IncomingMessage);
1585 }
1586
1587
1588 /************************************************************************
1589 * FUNCTION: GAME_IsMissionAccomplied
1590 * HISTORY:
1591 * 16.08.95 JC Created.
1592 * 23.08.95 JC added iKilledPlayer.
1593 * PURPOSE:
1594 * NOTES:
1595 ************************************************************************/
1596 Flag GAME_IsMissionAccomplied(Int32 iPlayer, Int32 iKilledPlayer)
1597 {
1598 Int32 piCount [NUM_CONTINENTS];
1599 Int32 i, cont, n1, n2;
1600
1601 switch (RISK_GetMissionTypeOfPlayer(iPlayer))
1602 {
1603 case CONQUIER_WORLD:
1604 if (RISK_GetNumCountriesOfPlayer(iPlayer) >= NUM_COUNTRIES)
1605 return TRUE;
1606 break;
1607 case CONQUIER_Nb_COUNTRY:
1608 n1 = RISK_GetMissionNumberOfPlayer(iPlayer);
1609 if (RISK_GetNumCountriesOfPlayer(iPlayer) >= n1)
1610 return TRUE;
1611 break;
1612 case CONQUIER_TWO_CONTINENTS:
1613 for (cont=0; cont<NUM_CONTINENTS; cont++)
1614 piCount[cont] = 0;
1615 for (i=0; i<NUM_COUNTRIES; i++)
1616 if (RISK_GetOwnerOfCountry(i) == iPlayer)
1617 piCount[RISK_GetContinentOfCountry(i)]++;
1618 n1 = RISK_GetMissionContinent1OfPlayer(iPlayer);
1619 n2 = RISK_GetMissionContinent2OfPlayer(iPlayer);
1620 if ( (piCount[n1] == RISK_GetNumCountriesOfContinent(n1))
1621 && (piCount[n2] == RISK_GetNumCountriesOfContinent(n2)))
1622 return TRUE;
1623 break;
1624 case KILL_A_PLAYER:
1625 n1 = RISK_GetMissionPlayerToKillOfPlayer(iPlayer);
1626 if ( (RISK_GetNumCountriesOfPlayer(n1) <= 0)
1627 && (n1 == iKilledPlayer))
1628 {
1629 RISK_SetMissionPlayerIsKilledOfPlayer(iPlayer, TRUE);
1630 return TRUE;
1631 }
1632 else if (RISK_GetMissionIsPlayerKilledOfPlayer(iPlayer))
1633 return TRUE;
1634 break;
1635 }
1636 return FALSE;
1637 }
1638
1639
1640 /************************************************************************
1641 * FUNCTION: GAME_ShowMission
1642 * HISTORY:
1643 * 16.08.95 JC Created.
1644 * PURPOSE:
1645 * NOTES:
1646 ************************************************************************/
1647 void GAME_ShowMission(Int32 iPlayer)
1648 {
1649 char buf[256];
1650 GAME_MissionToStr(iPlayer, RISK_GetMissionTypeOfPlayer(iPlayer),
1651 RISK_GetMissionContinent1OfPlayer(iPlayer),
1652 RISK_GetMissionContinent2OfPlayer(iPlayer),
1653 GAME_IsMissionAccomplied(iPlayer, -1),
1654 buf, sizeof(buf));
1655 UTIL_DisplayError(buf);
1656 }
1657