1 | /*
2 | * $Log: ml_press.c,v $
3 | * Revision 1.12 2004/07/12 00:29:09 millis
4 | * Don't show normal press warnings if game is PAUSED.
5 | *
6 | * Revision 1.11 2004/03/28 10:17:06 millis
7 | * Fix Bug 281: generate error if press rejected by MustOrder flag.
8 | *
9 | * Revision 1.10 2004/02/18 00:11:06 millis
10 | * Fix bug 275, not checking for touch press when game terminated.
11 | *
12 | * Revision 1.9 2003/09/13 22:54:51 millis
13 | * fix bug 228. Also prohibit press to non-masters while game is paused.
14 | *
15 | * Revision 1.8 2003/08/10 15:27:51 millis
16 | * Fix bug 25 (Add TouchPress)
17 | *
18 | * Revision 1.7 2003/06/20 23:25:37 millis
19 | * Fix bug 180, missing press messages (due to too much stack variables)
20 | *
21 | * Revision 1.6 2002/06/11 16:26:19 nzmb
22 | *
23 | * Added set [no]mustorder to require players to submit avalid set of orders
24 | * before they may send press (to be used in conjunction with set wait).
25 | *
26 | * Revision 1.5 2002/04/06 17:14:08 nzmb
27 | *
28 | * Changed ml_press.c so that press from late players isn't blocked when the game is over (again)!
29 | *
30 | * Revision 1.4 2002/03/14 03:12:26 nzmb
31 | *
32 | * Added fix to ml_press.c to prevent people from getting warning messages (of if nno late press is on being prohibited from) sending press when the game is over.
33 | *
34 | * Revision 1.3 2001/05/10 09:05:05 greg
35 | * added subjectlines
36 | *
37 | * Revision 1.2 2000/11/14 14:27:37 miller
38 | * Various changes including
39 | * - Partial press between master and only one power is NOT sent to others with WATCHALL set
40 | * - Rejct press in a monor phase when XF_NOMINORPRESS is set
41 | * - Change press handling to allow observers to press in no-press games f allowed (i.e. BackSeat Driver variant)
42 | * - Add prevention of press from late players, if flag is set
43 | *
44 | * Revision 1.1 1998/02/28 17:49:42 david
45 | * Initial revision
46 | *
47 | * Revision 1.1 1996/10/20 12:29:45 rpaar
48 | * Morrolan v9.0
49 | */
50 |
51 | /*
52 | * ml_press.c -- Process 'Press' and 'Broadcast' Commands
53 | *
54 | * Copyright (C) 1987, Ken Lowe
55 | *
56 | * Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
57 | * Maryland, all rights reserved; used with permission.
58 | *
59 | * Redistribution and use in source and binary forms are permitted
60 | * provided that it is for non-profit purposes, that this and the
61 | * above notices are preserved and that due credit is given to Mr.
62 | * Lowe.
63 | *
64 | * REVISION HISTORY
65 | * DATE NAME REASON
66 | * ----------- ------------ -----------------------------------------------
67 | * ?? ??? 1987 K.Lowe Original development.
68 | * 10 Mar 1993 Positron Tell Master the truth about the message.
69 | * 31 Mar 1994 C.Marcus,Jr. Complete redesign/rewrite.
70 | * 28 Apr 1994 C.Marcus,Jr. Always include sender's address to Master.
71 | * 23 May 1994 C.Marcus,Jr. Correct parsing of press options.
72 | * 27 Nov 1999 M. Miller Prohibit partial press from late players
73 | * 07 Dec 1999 M. Miller parse_power doesn't allow duplicates
74 | * 01 Jun 2002 T. Miller Support for must_order flag
75 | */
76 |
77 | #include <stdlib.h>
78 | #include <string.h>
79 |
80 | #include "dip.h"
81 | #include "mail.h"
82 | #include "functions.h"
83 | #include "porder.h"
84 | extern int errorflag;
85 |
86 | /*
87 | * Declarations & Definitions
88 | */
89 |
90 | enum {
91 | NONE = 0, /* Unrecognized option */
92 | GREY, /* Grey press */
93 | WHITE, /* White press */
94 | PARTIAL, /* Press to selected individuals */
95 | ALLBUT, /* To all but selected individuals */
96 | FAKEB, /* Faked as broadcast */
97 | FAKEP, /* Faked as to selected individuals */
98 | FAKEA, /* Faked as not to selected individuals */
99 | NOFAKEB, /* Override default of faked broadcasts */
100 | };
101 |
102 | #define is_observer(x) (dipent.players[x].power == OBSERVER)
103 |
104 | /*
105 | * Public Function Declarations
106 | */
107 |
108 | void mail_press(char *s, int need_opts);
109 |
110 | /*
111 | * Static Function Declarations
112 | */
113 |
114 | static void print_line(FILE * outf, char *line);
115 | static int IsAdjacent(int asking_power, int other_power);
116 |
117 |
118 | /*
119 | * Static Data Object Definitions
120 | */
121 |
122 | static char *options[] =
123 | {"", "grey", "white", "anonymous", "unanonymous",
124 | "anonymously", "unanonymously", "gray",
125 | "-", "to all but", "+", "to",
126 | "fake partial to all but", "fake parital -", "fake -",
127 | "fake to all but", "fake partial to", "fake to",
128 | "fake +", "fake broadcast", "no fake broadcast",
129 | "fake", "no fake "};
130 |
131 | static int values[] =
132 | {NONE, GREY, WHITE, GREY, WHITE,
133 | GREY, WHITE, GREY,
134 | ALLBUT, ALLBUT, PARTIAL, PARTIAL,
135 | FAKEA, FAKEA, FAKEA,
136 | FAKEA, FAKEP, FAKEP,
137 | FAKEP, FAKEB, NOFAKEB,
138 | FAKEB, NOFAKEB};
139 |
140 | /*******************************************************************************
141 | NAME: mail_press
142 |
143 | DESCRIPTION
144 | This procedure processes a 'press' or 'broadcast' command. It parses the
145 | rest of the command line (if any), checks the validity of the command, and
146 | if valid then sets the appropriate flags so that the main command parser
147 | will interpret the following lines as a press message (to be either sent or
148 | ignored, as necessary).
149 |
150 | Press commands can have up to three types of options: a colour (white or
151 | grey), a partial delivery list (either players to send the message to, or
152 | players not to send the message to), and a 'fake' option (fake the message
153 | as a broadcast, fake it as going to certain players, fake it as not going
154 | to certain players, or override a default fake broadcast). None of these
155 | options can be specified more than once.
156 |
157 | REVISION HISTORY
158 | DATE NAME REASON
159 | ----------- ------------ ---------------------------------------------------
160 | ?? ??? 1987 K.Lowe Original development.
161 | 10 Mar 1993 Positron Tell Master the truth about the source and desti-
162 | nation(s) of the press/broadcast message.
163 | 31 Mar 1994 C.Marcus,Jr. Complete redesign/rewrite, to allow 'press to m' to
164 | always work no matter what press settings are.
165 | 28 Apr 1994 C.Marcus,Jr. Always include the sender's address in press to
166 | Master.
167 | 23 May 1994 C.Marcus,Jr. Correct parsing of press options.
168 |
169 | INTERFACE DEFINITION
170 | Calling Sequence:
171 | mail_press(s, need_opts);
172 |
173 | Inputs:
174 | s := Pointer to command line, following 'press' or 'broadcast'
175 | keyword
176 | need_opts := TRUE if options must be specified, FALSE if options are
177 | optional (sorry about that wording!)
178 |
179 | Outputs:
180 | broadcast (global) := TRUE if command is good, else FALSE
181 | broad_part (global) := TRUE if press is only to some players, FALSE
182 | if press is to everybody
183 | broad_allbut (global) := TRUE if partial list is list of players to
184 | *not* receive message, FALSE if partial list
185 | is players to receive message
186 | broad_read (global) := TRUE if message is to be read and sent, else
187 | FALSE
188 | broad_skip (global) := TRUE if message is to be read and discarded,
189 | else FALSE
190 | broad_list (global) := List of power letters to which message is to
191 | be sent (broad_allbut = FALSE) or not sent
192 | (broad_allbut = TRUE)
193 |
194 | ALGORITHM
195 | [Note: global return variables were initialized when broadcast files were
196 | opened, and do not need to be initialized here.]
197 |
198 | Initialize local flags as necessary.
199 | Parse the command line options, if any, setting local flags only.
200 | Validate command options against game flags.
201 | If command is valid, write press header to broadcast and/or master's broad-
202 | cast file(s).
203 | Set global return variables according to validity of local flags.
204 | *******************************************************************************/
205 |
206 | void mail_press(char *s, int need_opts)
207 | {
208 | /*
209 | * Dynamic Data Object Definitions
210 | */
211 | int bad_cmd = 0, /* Is this a bad command? */
212 | have_opts = 0; /* Have we processed any options? */
213 | static char fake_list[sizeof(broad_list)], /* List of fake destination powers */
214 | game_name[32], /* " in <name>:" string buffer */
215 | line[1024], /* Line to be sent to other players */
216 | mline[1024], /* Line to be sent to Master */
217 | part_list[sizeof(broad_list)];
218 | /* List of destination powers */
219 | int color = NONE, /* Is press grey, white, or default? */
220 | count = 0, /* Number of entries in part_list */
221 | fake = NONE, /* Is this message faked? */
222 | fake_count = 0, /* Number of entries in fake_list */
223 | i, /* Return value from lookfor() */
224 | partial = NONE; /* Is this message to some only? */
225 | char lpower;
226 | int xctr;
227 |
228 | /*
229 | * End of Definitions
230 | */
231 |
232 | /* Parse the command line options, if any, setting local flags only. */
233 | sprintf(subjectline, "%s:%s - %s Diplomacy Notice ml_press", JUDGE_CODE, dipent.name, dipent.phase);
234 |
235 | master_press = 0;
236 | master_only_press = 0;
237 |
238 | while (*s != '\0') {
239 | s = lookfor(s, options, nentry(options), &i);
240 | if (!i) {
241 | while (isspace(*s))
242 | ++s;
243 | if (*s != '\0') {
244 | fprintf(rfp, "Unrecognized press/broadcast option '%s'.\n", s);
245 | bad_cmd = 1;
246 | }
247 | break;
248 | } else {
249 | have_opts = 1;
250 | switch (values[i]) {
251 |
252 | case GREY:
253 | if (color) {
254 | fprintf(rfp,
255 | "Multiple 'colors' specified in press command.\n");
256 | bad_cmd = 1;
257 | } else {
258 | color = GREY;
259 | }
260 | break;
261 |
262 | case WHITE:
263 | if (color) {
264 | fprintf(rfp,
265 | "Multiple 'colors' specified in press command.\n");
266 | bad_cmd = 1;
267 | } else {
268 | color = WHITE;
269 | }
270 | break;
271 |
272 | case PARTIAL:
273 | if (partial) {
274 | fprintf(rfp,
275 | "Conflicting options specified in press command.\n");
276 | bad_cmd = 1;
277 | } else {
278 | partial = PARTIAL;
279 | }
280 | bad_cmd |= parse_powers(&s, part_list, sizeof(part_list),
281 | &count,1);
282 | break;
283 |
284 | case ALLBUT:
285 | if (partial) {
286 | fprintf(rfp,
287 | "Conflicting options specified in press command.\n");
288 | bad_cmd = 1;
289 | } else {
290 | partial = ALLBUT;
291 | }
292 | bad_cmd |= parse_powers(&s, part_list, sizeof(part_list),
293 | &count,1);
294 | break;
295 |
296 | case FAKEB:
297 | if (fake) {
298 | fprintf(rfp,
299 | "Conflicting options specified in press command.\n");
300 | bad_cmd = 1;
301 | } else {
302 | fake = FAKEB;
303 | }
304 | break;
305 |
306 | case FAKEP:
307 | if (fake) {
308 | fprintf(rfp,
309 | "Conflicting options specified in press command.\n");
310 | bad_cmd = 1;
311 | } else {
312 | fake = FAKEP;
313 | }
314 | bad_cmd |= parse_powers(&s, fake_list, sizeof(fake_list),
315 | &fake_count,1);
316 | break;
317 |
318 | case FAKEA:
319 | if (fake) {
320 | fprintf(rfp,
321 | "Conflicting options specified in press command.\n");
322 | bad_cmd = 1;
323 | } else {
324 | fake = FAKEA;
325 | }
326 | bad_cmd |= parse_powers(&s, fake_list, sizeof(fake_list),
327 | &fake_count,1);
328 | break;
329 |
330 | case NOFAKEB:
331 | if (fake) {
332 | fprintf(rfp,
333 | "Conflicting options specified in press command.\n");
334 | bad_cmd = 1;
335 | } else {
336 | fake = NOFAKEB;
337 | }
338 | break;
339 | }
340 | }
341 | }
342 |
343 | /* Validate command options against game flags: first, if options were
344 | required, were they specified? */
345 |
346 | if (need_opts && !have_opts) {
347 | fprintf(rfp,
348 | "You have specified no options. If you want to send a plain\n%s",
349 | "message to everyone, please use the BROADCAST command.\n");
350 | bad_cmd = 1;
351 |
352 | /* Master can send any kind of press he wants, no matter what the settings. */
353 |
354 | } else if (dipent.players[player].power == MASTER) {
355 | master_press = 1;
356 | /* Good command by definition, so don't do any more checking. */
357 | if (partial == PARTIAL && count == 1) {
358 | /* Keep this between master and sender only */
359 | master_only_press = 1;
360 | }
361 |
362 | /* Anyone can send to Master (only), no matter what the settings (but it won't
363 | be grey press, and he better not try to fake it!). */
364 |
365 | } else if ((partial == PARTIAL) && (count == 1) &&
366 | (power(part_list[0]) == MASTER)) {
367 | color = WHITE;
368 | master_press = 1;
369 | master_only_press = 1; /* Only for master and sender */
370 | if (fake) {
371 | fprintf(rfp,
372 | "Shame! Attempting to fake out the Master is not allowed.\n");
373 | fake = NONE;
374 | }
375 | /* Checking for normal (i.e., not from or to Master) press: first, is press
376 | allowed from non-observers (i.e. players)? */
377 |
378 | } else {
379 | if ((dipent.flags & F_NOWHITE) && !(dipent.flags & F_GREY) && !is_observer(player)) {
380 | fprintf(rfp, "Game '%s' does not allow press.\n", dipent.name);
381 | bad_cmd = 1;
382 |
383 | /* If this is from an Observer, is observer press allowed? */
384 |
385 | } else if ((dipent.flags & F_OBNONE) && is_observer(player)) {
386 | fprintf(rfp, "Game '%s' does not allow observer press.\n",
387 | dipent.name);
388 | bad_cmd = 1;
389 |
390 | /* If a minor phase and no minor press allowed, reject it */
391 | } else if ((dipent.xflags & XF_NOMINORPRESS) &&
392 | (dipent.phase[5] == 'B' || dipent.phase[5] == 'R')) {
393 | fprintf(rfp, "Game '%s' does not allow press in minor phases.\n",
394 | dipent.name);
395 | bad_cmd = 1;
396 |
397 | /* If this is grey press, is it allowed? */
398 |
399 | } else {
400 | if (color == GREY) {
401 | if (!(dipent.flags & F_GREY)) {
402 | fprintf(rfp, "Game '%s' does not allow grey press.\n",
403 | dipent.name);
404 | bad_cmd = 1;
405 | } else if ((dipent.flags & F_OBWHITE) && is_observer(player)) {
406 | fprintf(rfp,
407 | "Game '%s' does not allow grey press from observers.\n",
408 | dipent.name);
409 | bad_cmd = 1;
410 | }
411 | /* If this is white press, is it allowed? */
412 |
413 | } else if (color == WHITE) {
414 | if (dipent.flags & F_NOWHITE) {
415 | fprintf(rfp, "Game '%s' does not allow white press.\n",
416 | dipent.name);
417 | bad_cmd = 1;
418 | }
419 | /* Colour not specified; set default colour (Master defaults to white press;
420 | observers default to white if ObWhite is set; otherwise, default
421 | is grey if NoWhite is set, or if Grey is set and DefWhite is NOT set, or
422 | white in any other case). */
423 |
424 | } else {
425 | if (((dipent.flags & F_OBWHITE) && is_observer(player)) ||
426 | (dipent.players[player].power == MASTER)) {
427 | color = WHITE;
428 | } else {
429 | color = (((dipent.flags & F_GREY) &&
430 | !(dipent.flags & F_DEFWHITE)) ||
431 | (dipent.flags & F_NOWHITE)) ? GREY : WHITE;
432 | }
433 | }
434 |
435 | /* If this is partial press, is it allowed? */
436 |
437 | if (partial) {
438 | if (is_observer(player)) {
439 | if (dipent.flags & (F_OBNONE | F_OBWHITE )) {
440 | fprintf(rfp,"Game '%s' does not allow observer partial press.\n",
441 | dipent.name);
442 | bad_cmd = 1;
443 | }
444 | } else {
445 | if (dipent.flags & F_NOPARTIAL) {
446 | fprintf(rfp, "Game '%s' does not allow partial press.\n",
447 | dipent.name);
448 | bad_cmd = 1;
449 | }
450 | }
451 | }
452 |
453 | /* If this is a no-fake-broadcast, is it allowed? */
454 |
455 | if (fake == NOFAKEB) {
456 | if (!(dipent.flags & F_FAKE) && (dipent.flags & F_DEFFAKE)) {
457 | fprintf(rfp,
458 | "Game '%s' only allows partial press to fake broadcast.\n",
459 | dipent.name);
460 | bad_cmd = 1;
461 | }
462 | /* If this is a fake broadcast, is it allowed? */
463 |
464 | } else if (fake == FAKEB) {
465 | if (!(dipent.flags & F_FAKE) && !(dipent.flags & F_DEFFAKE)) {
466 | fprintf(rfp, "Game '%s' does not allow fake press.\n",
467 | dipent.name);
468 | bad_cmd = 1;
469 | }
470 | /* If this is a fake partial, is it allowed? */
471 |
472 | } else if (fake) {
473 | if (dipent.flags & F_NOPARTIAL) {
474 | fprintf(rfp, "Game '%s' does not allow partial press.\n",
475 | dipent.name);
476 | bad_cmd = 1;
477 | } else if (!(dipent.flags & F_FAKE) &&
478 | (dipent.flags & F_DEFFAKE)) {
479 | fprintf(rfp,
480 | "Game '%s' only allows partial press to fake broadcast.\n",
481 | dipent.name);
482 | bad_cmd = 1;
483 | } else if (!(dipent.flags & F_FAKE)) {
484 | fprintf(rfp, "Game '%s' does not allow fake press.\n",
485 | dipent.name);
486 | bad_cmd = 1;
487 | }
488 | /* Fake option not specified; default to fake broadcast if message is partial
489 | and partial fakes broadcast. */
490 |
491 | } else {
492 | if (partial && (dipent.flags & F_DEFFAKE)) {
493 | fake = FAKEB;
494 | }
495 | }
496 | }
497 | }
498 |
499 | /* Have we submitted orders yet? */
500 | if (!(dipent.phase[6] == 'X') && !GAME_PAUSED)
501 | {
502 | if((!(dipent.players[player].status & SF_MOVED)) && (dipent.players[player].status & SF_MOVE) && (dipent.x2flags & X2F_MUSTORDER))
503 | {
504 | if(!((count == 1) && (power(part_list[0]) == MASTER)))
505 | {
506 | /* don't want to send this ... */
507 | fprintf(rfp,"Game %s does not allow press from players who have not sent complete orders.\n", dipent.name);
508 | bad_cmd = 1;
509 | }
510 | }
511 | /* OK, let's see if press from late powers is allowed */
512 |
513 | if (WAITING(dipent.players[player].status)) {
514 | /* it is not, let us see if this power is late and not pressing to master */
515 | if (!((count == 1) && (power(part_list[0]) == MASTER))) {
516 | /* We are waiting for him, see if messag is only to master */
517 | if (time(NULL) > dipent.deadline) {
518 | if (dipent.xflags & XF_NOLATEPRESS) {
519 | fprintf(rfp, "Game '%s' does not allow press from late players.\n",
520 | dipent.name);
521 | fprintf(rfp, "You must submit moves for ALL your units before pressing.\n");
522 | bad_cmd = 1;
523 | } else {
524 | /* you can press when late, but it's not really good so... */
525 | fprintf(rfp,
526 | "Note: You are marked as late and should really have\n corrected this before sending press.\n\n");
527 | }
528 | }
529 | }
530 | }
531 | }
532 |
533 | if (partial && !GAME_PAUSED) {
534 | if (dipent.x2flags & X2F_TOUCHPRESS && !master_press && dipent.phase[6] != 'X') {
535 | xctr =0;
536 | while ((part_list[xctr] = toupper(part_list[xctr])) != '\0') {
537 | if (!IsAdjacent(dipent.players[player].power, power(part_list[xctr]))) {
538 | fprintf(rfp, "You are not currently adjacent to %s.\n",
539 | powers[power(part_list[xctr])]);
540 | bad_cmd = 1;
541 | }
542 | xctr++;
543 | }
544 | }
545 | } else if (!GAME_PAUSED) {
546 | if (dipent.x2flags & X2F_TOUCHPRESS && !master_press && dipent.phase[6] != 'X') {
547 | if ( dipent.players[player].status & SF_BROAD_SENT) {
548 | fprintf(rfp,"Sorry, you have already sent one broadcast: wait until next turn!\n");
549 | bad_cmd = 1;
550 | } else {
551 | dipent.players[player].status |= SF_BROAD_SENT;
552 | }
553 | }
554 | }
555 |
556 | if (GAME_PAUSED && !master_press && !master_only_press) {
557 | fprintf(rfp,"Sorry, you cannot press (except to the master alone) when the game is paused.\n");
558 | bad_cmd = 1;
559 | }
560 |
561 |
562 | /* If command is invalid, discard the following text. */
563 |
564 | if (bad_cmd) {
565 | fprintf(rfp, "\nNo press/broadcast message sent, discarding text:\n\n");
566 | broad_skip = 1;
567 | errorflag++; /* Bug 281, set error flag if press rejected */
568 |
569 | /* If command is valid, write press header to broadcast and/or master's
570 | broadcast file(s): first, tell the sender what we're doing. */
571 |
572 | } else {
573 | if (partial) {
574 | sprintf(line, "Message sent to %s", (partial == ALLBUT) ?
575 | "all but " : "");
576 | list_powers(line, part_list);
577 | strcat(line, ":\n");
578 | } else {
579 | strcpy(line, "Broadcast message sent:\n");
580 | }
581 | print_line(rfp, line);
582 |
583 | /* Tell the recipients what we want them to believe, and tell the Master the
584 | truth: if it's grey partial press (or we want them to think so), the other
585 | players get "Message to ", while the Master gets "Message [from <address> as
586 | <power>] to ". */
587 |
588 | if ((partial && (fake != FAKEB)) || (fake == FAKEP) || (fake == FAKEA)) {
589 | if (color == GREY) {
590 | sprintf(subjectline, "%s:%s - %s Press to ", JUDGE_CODE, dipent.name, dipent.phase);
591 |
592 | strcpy(line, "Message to ");
593 | sprintf(mline, "Message [from %s as %s] to ",
594 | dipent.players[player].address,
595 | powers[dipent.players[player].power]);
596 |
597 | /* If it's white partial press, either in a non-Gunboat game or from the
598 | Master, the other players and the Master get "Message from <address> as
599 | <power> to ". */
600 |
601 | } else {
602 | if (!(dipent.flags & F_GUNBOAT) ||
603 | (dipent.players[player].power == MASTER)) {
604 | lpower = dipent.pl[dipent.players[player].power];
605 | sprintf(subjectline, "%s:%s - %s Press from %c to ", JUDGE_CODE, dipent.name, dipent.phase, lpower);
606 |
607 | sprintf(line, "Message from %s as %s to ", raddr,
608 | powers[dipent.players[player].power]);
609 | sprintf(mline, "Message from %s as %s to ", raddr,
610 | powers[dipent.players[player].power]);
611 |
612 | /* If it's white partial press from an observer or player (Gunboat game), the
613 | other players get "Message from <power> to ", while the Master gets "Message
614 | from [<address> as] <power> to ". */
615 |
616 | } else {
617 | lpower = dipent.pl[dipent.players[player].power];
618 | sprintf(subjectline, "%s:%s - %s Press from %c to ", JUDGE_CODE, dipent.name, dipent.phase, lpower);
619 |
620 | sprintf(line, "Message from %s to ",
621 | powers[dipent.players[player].power]);
622 | sprintf(mline, "Message from [%s as] %s to ",
623 | dipent.players[player].address,
624 | powers[dipent.players[player].power]);
625 | }
626 | }
627 |
628 | /* If the partial press is to all but certain powers (or we wish them to think
629 | so), add "all but ". */
630 |
631 | if ((fake == FAKEA) || (!fake && (partial == ALLBUT))) {
632 | strcat(subjectline, "all but ");
633 | strcat(line, "all but ");
634 | strcat(mline, "all but ");
635 | }
636 | /* List the destination (or supposed destination) powers. */
637 | xctr = 0;
638 | while ((fake_list[xctr] = toupper(fake_list[xctr])) != '\0') xctr++;
639 | xctr =0;
640 | while ((part_list[xctr] = toupper(part_list[xctr])) != '\0') xctr++;
641 |
642 | strcat(subjectline, ((fake == FAKEP) || (fake == FAKEA)) ? fake_list :
643 | part_list);
644 |
645 | list_powers(line, ((fake == FAKEP) || (fake == FAKEA)) ? fake_list :
646 | part_list);
647 | list_powers(mline, ((fake == FAKEP) || (fake == FAKEA)) ? fake_list
648 | : part_list);
649 |
650 | /* If it's grey broadcast press (or we want them to think so), the other
651 | players get "Broadcast message", while the Master gets "Broadcast message
652 | [from <address> as <power>]". */
653 |
654 | } else {
655 | if (color == GREY) {
656 | sprintf(subjectline, "%s:%s - %s Broadcast", JUDGE_CODE, dipent.name, dipent.phase);
657 |
658 | strcpy(line, "Broadcast message");
659 | sprintf(mline, "Broadcast message [from %s as %s]",
660 | dipent.players[player].address,
661 | powers[dipent.players[player].power]);
662 |
663 | /* If it's white broadcast press, either in a non-Gunboat game or from the
664 | Master, the other players and the Master get "Broadcast message from
665 | <address> as <power>". */
666 |
667 | } else {
668 | if (!(dipent.flags & F_GUNBOAT) ||
669 | (dipent.players[player].power == MASTER)) {
670 | lpower = dipent.pl[dipent.players[player].power];
671 | sprintf(subjectline, "%s:%s - %s Broadcast from %c", JUDGE_CODE, dipent.name, dipent.phase, lpower);
672 |
673 | sprintf(line, "Broadcast message from %s as %s", raddr,
674 | powers[dipent.players[player].power]);
675 | sprintf(mline, "Broadcast message from %s as %s", raddr,
676 | powers[dipent.players[player].power]);
677 |
678 | /* If it's white broadcast press from an observer or player (Gunboat game), the
679 | other players get "Broadcast message from <power>", while the Master gets
680 | "Broadcast message from [<address> as] <power>". */
681 |
682 | } else {
683 | lpower = dipent.pl[dipent.players[player].power];
684 | sprintf(subjectline, "%s:%s - %s Broadcast from %c", JUDGE_CODE, dipent.name, dipent.phase, lpower);
685 |
686 | sprintf(line, "Broadcast message from %s",
687 | powers[dipent.players[player].power]);
688 | sprintf(mline, "Broadcast message from [%s as] %s",
689 | dipent.players[player].address,
690 | powers[dipent.players[player].power]);
691 | }
692 | }
693 | }
694 |
695 |
696 | /* Make sure the Master knows who it's really to. */
697 |
698 | if (fake && (fake != NOFAKEB)) {
699 | if (partial) {
700 | if (partial == ALLBUT) {
701 | strcat(mline, " [really to all but ");
702 | } else {
703 | strcat(mline, " [really to ");
704 | }
705 | list_powers(mline, part_list);
706 | strcat(mline, "]");
707 | } else {
708 | strcat(mline, " [really to everyone]");
709 | }
710 | }
711 | /* Finish up with the game name, and print it. */
712 |
713 | sprintf(game_name, " in '%s':\n", dipent.name);
714 | strcat(line, game_name);
715 | strcat(mline, game_name);
716 | print_line(rfp, line);
717 | fprintf(bfp, "%s\n", line); /* The previous call has put in the new-
718 | lines except the extra at the end, so
719 | we don't have to call print_line(). */
720 | print_line(mbfp, mline);
721 |
722 | /* Set global return variables according to validity of local flags. */
723 |
724 | broadcast = 1;
725 | broad_part = (partial != NONE);
726 | broad_allbut = (partial == ALLBUT);
727 | broad_read = 1;
728 | if (partial)
729 | strcpy(broad_list, part_list);
730 | }
731 | return;
732 | }
733 | /*******************************************************************************
734 | NAME: parse_powers
735 |
736 | DESCRIPTION
737 | This procedure parses a list of power letters, checking that each letter is
738 | valid, and that not too many are specified, and stores them in the specified
739 | array.
740 |
741 | REVISION HISTORY
742 | DATE NAME REASON
743 | ----------- ------------ ---------------------------------------------------
744 | 31 Mar 1994 C.Marcus,Jr. Original development (extracted from original
745 | mail_press() procedure).
746 |
747 | INTERFACE DEFINITION
748 | Calling Sequence:
749 | error = parse_powers(&s, &list, size, &count);
750 |
751 | Inputs:
752 | s := Pointer to command line, at beginning of power letters list
753 | size := Size of list array
754 | put_output: =1 if to go to rfp, else not.
755 |
756 | Outputs:
757 | s := Updated pointer, at next character after power letters list
758 | error := TRUE if parsing error occurred, else FALSE
759 | list := Character array containing validated power letters
760 | count := Number of valid power letters parsed
761 |
762 | ALGORITHM
763 | Initialize count and list array, and skip any spaces in command line.
764 | While not at end of command line or at space:
765 | If next character is valid power letter, and room remains in array,
766 | store the power letter and count it, else issue error message.
767 | Endwhile.
768 | *******************************************************************************/
769 |
770 | int parse_powers(char **s, char *list, size_t size, int *count, int put_output)
771 | {
772 | /*
773 | * Dynamic Data Object Definitions
774 | */
775 | int error = 0; /* Parsing error occurred? */
776 | char *next = list; /* Place to store next letter */
777 | int power_used[MASTER+1];
778 | int i;
779 | /*
780 | * End of Definitions
781 | */
782 |
783 | /* Initialize count and list array, and skip any spaces in command line. */
784 |
785 | for (i=0; i <= MASTER; i++) power_used[i] = 0;
786 |
787 | *count = 0;
788 | memset((void *) list, (int) '\0', size);
789 | while (isspace(**s))
790 | ++(*s);
791 |
792 | /* While not at end of command line or at space: If next character is valid
793 | power letter, and room remains in array, store the power letter and count
794 | it, else issue error message. */
795 |
796 | while (**s && !isspace(**s)) {
797 | power_used[power(**s)]++;
798 | if (!power(**s)) {
799 | if (put_output) fprintf(rfp, "Unknown power specification '%c'.\n", **s);
800 | error = 1;
801 | } else if (next >= list + size) {
802 | if (put_output) fprintf(rfp, "Too many power specifications.\n");
803 | error = 1;
804 | } else if (power_used[power(**s)] == 2 ) {
805 | if (put_output) fprintf(rfp, "Power '%c' repeated in list. \n", **s);
806 | error = 1;
807 | } else {
808 | *next++ = **s;
809 | ++(*count);
810 | }
811 | ++(*s);
812 | }
813 | return error;
814 | }
815 | /*******************************************************************************
816 | NAME: list_powers
817 |
818 | DESCRIPTION
819 | This procedure takes a list of power letters, and adds the corresponding
820 | names to the indicated line to be printed out.
821 |
822 | REVISION HISTORY
823 | DATE NAME REASON
824 | ----------- ------------ ---------------------------------------------------
825 | 31 Mar 1994 C.Marcus,Jr. Original development (extracted from original
826 | mail_press() procedure).
827 |
828 | INTERFACE DEFINITION
829 | Calling Sequence:
830 | list_powers(&line, &list);
831 |
832 | Inputs:
833 | list := Character array containing valid power letters
834 |
835 | Outputs:
836 | line := Character string to which power names are concatenated
837 |
838 | ALGORITHM
839 | For each letter in list:
840 | Concatenate corresponding power name to output line.
841 | If two or more letters remain, add a comma; else, if only one letter
842 | remains, add "and".
843 | Endloop.
844 | *******************************************************************************/
845 |
846 | void list_powers(char *line, char *list)
847 | {
848 | /*
849 | * Dynamic Data Object Definitions
850 | */
851 |
852 | /*
853 | * End of Definitions
854 | */
855 |
856 | /* For each letter in list: Concatenate corresponding power name to output
857 | line. */
858 |
859 | for ( /* list pointer already set */ ; *list; ++list) {
860 | strcat(line, powers[power(*list)]);
861 |
862 | /* If two or more letters remain, add a comma; else, if only one letter
863 | remains, add "and". */
864 |
865 | if (*(list + 1)) {
866 | if (*(list + 2)) {
867 | strcat(line, ", ");
868 | } else {
869 | strcat(line, " and ");
870 | }
871 | }
872 | }
873 | return;
874 | }
875 | /*******************************************************************************
876 | NAME: print_line
877 |
878 | DESCRIPTION
879 | This procedure takes a line of output text, breaks it into pieces of a
880 | defined size or smaller (breaking only at spaces), and prints it to the
881 | specified output file. An additional newline is added at the end of the
882 | line. It is assumed that there are no imbedded newlines in the line before
883 | processing starts (except possibly the last character, if a following blank
884 | line is desired).
885 |
886 | Note that upon return, the newlines inserted to break up the line are still
887 | present, so that the line may be printed again (perhaps to a different out-
888 | put file) without calling print_line() a second time. However, the termina-
889 | ting newline is not added to the array.
890 |
891 | REVISION HISTORY
892 | DATE NAME REASON
893 | ----------- ------------ ---------------------------------------------------
894 | 31 Mar 1994 C.Marcus,Jr. Original development (extracted from original
895 | mail_press() procedure).
896 |
897 | INTERFACE DEFINITION
898 | Calling Sequence:
899 | print_line(outf, &line);
900 |
901 | Inputs:
902 | outf := File pointer to output file
903 | line := Character string to be printed
904 |
905 | Outputs:
906 | line := Character string as printed, possibly with embedded newlines
907 | Text written to specified output file.
908 |
909 | ALGORITHM
910 | Loop until length of last segment is less than cutoff point:
911 | Starting from cutoff point, search backward for last space, and change
912 | it to a newline.
913 | Endloop.
914 | Print the line (with terminating newline).
915 | *******************************************************************************/
916 |
917 | static void print_line(FILE * outf, char *line)
918 | {
919 | /*
920 | * Dynamic Data Object Definitions
921 | */
922 | char *head, *tail; /* Ptrs. to ends of string segment */
923 | /*
924 | * End of Definitions
925 | */
926 |
927 | /* Loop until length of last segment is less than cutoff point: Starting
928 | from cutoff point, search backward for last space, and change it to a new-
929 | line. */
930 |
931 | for (head = line, tail = line + CUTOFF_LENGTH;
932 | strlen(head) > CUTOFF_LENGTH;
933 | head = tail + 1, tail = head + CUTOFF_LENGTH) {
934 | while (!isspace(*tail))
935 | --tail;
936 | *tail = '\n';
937 | }
938 |
939 | /* Print the line (with terminating newline). */
940 |
941 | fprintf(outf, "%s\n", line);
942 | return;
943 | }
944 |
945 | /* Find out if a power is adjacent to another */
946 |
947 | static int IsAdjacent(int asking_power, int other_power)
948 | {
949 | int u, u1, c=0, b=0, adjacent = 0;
950 |
951 | if (asking_power == other_power)
952 | return 1; /* Always adjacent to self! */
953 |
954 | if (other_power == OBSERVER)
955 | return 0; /* Never close to observer */
956 |
957 | if (other_power == MASTER)
958 | return 1; /* Always close to master */
959 |
960 | if (!dipent.pr_valid) {
961 | /* pr[] must be filled to run this code */
962 | po_init();
963 | gamein();
964 | dipent.pr_valid++;
965 | }
966 |
967 |
968 | /* OK, look though all my units, and see if anyone of them is next to other power's units */
969 |
970 | /* Do this by pretending to be a wing, and seeing if can move there */
971 |
972 | for (u=1; u <= nunit && !adjacent; u++) {
973 | if (unit[u].owner == asking_power) {
974 | unit[++nunit].type = 'W';
975 | unit[nunit].loc = unit[u].loc; /* Add dummy wing unit to test moveability */
976 | for (u1 = 1; u1 < nunit && !adjacent; u1++) {
977 | if (unit[u1].owner == other_power)
978 | if (valid_move(nunit, unit[u1].loc, &c, &b))
979 | adjacent++;
980 | }
981 | nunit--; /* Remove dummy unit */
982 | }
983 | }
984 |
985 | return adjacent;
986 | }
987 |