1    | /*
2    |    ** $Log: st_retreat.c,v $
3    |    ** Revision 1.11  2004/10/23 19:03:03  millis
4    |    ** Fixed bug 376 (allow player self-dislodged units to retreat in duplex)
5    |    **
6    |    ** Revision 1.10  2004/07/25 16:13:45  millis
7    |    ** Bug fixes for Bug 91 (Duplex powers), Bug 233 (Abandoned power cannot
8    |    ** return in duplex) and Bug 206 (allow takeover of unknown abandoned
9    |    ** countries)
10   |    **
11   |    ** Revision 1.9  2004/06/09 22:05:09  millis
12   |    ** More fixes for Bug 297, Intimate Diplomacy
13   |    **
14   |    ** Revision 1.8  2003/08/10 00:57:39  millis
15   |    ** Fix bug 214
16   |    **
17   |    ** Revision 1.7  2003/05/10 00:46:15  millis
18   |    ** Bug 140 fix, display 'orders' when orders and 'results' when results
19   |    **
20   |    ** Revision 1.6  2003/01/18 14:31:39  millis
21   |    ** Implements half-strength retreats from USTV (for 1900 variant)
22   |    **
23   |    ** Revision 1.5.2.1  2003/01/13 16:04:58  millis
24   |    ** ustv latest versions
25   |    **
26   |    ** Revision 1.5  2002/04/15 12:55:47  miller
27   |    ** Multiple changes for blind & Colonial & setup from USTV
28   |    **
29   |    ** Revision 1.4  2001/07/08 23:04:09  miller
30   |    ** Add predict flag
31   |    **
32   |    ** Revision 1.3  2000/11/14 14:27:37  miller
33   |    ** Allow wing units
34   |    ** REcalcultate blockade settings
35   |    ** Allow retreat moves syntax checking
36   |    **
37   |    ** Revision 1.2  1999/07/13 19:55:05  davidn
38   |    ** Size of array increased as array is indexed by owner number, not index of
39   |    ** player in master file entry.
40   |    ** Was causing core dump on USTR July 1999
41   |    **
42   |    ** Revision 1.1  1998/02/28 17:49:42  david
43   |    ** Initial revision
44   |    **
45   |    ** Revision 1.1  1996/10/20 12:29:45  rpaar
46   |    ** Morrolan v9.0
47   |    **
48   |  */
49   | 
50   | /*  st_retreat.c
51   |    **
52   |    **  Copyright 1987, Lowe.
53   |    **
54   |    **  Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
55   |    **  Maryland, all rights reserved; used with permission.
56   |    **
57   |    **  Redistribution and use in source and binary forms are permitted
58   |    **  provided that it is for non-profit purposes, that this and the
59   |    **  above notices are preserved and that due credit is given to Mr.
60   |    **  Lowe.
61   |    **
62   |  */
63   | 
64   | #include <stdlib.h>
65   | #include <string.h>
66   | 
67   | #include "dip.h"
68   | #include "functions.h"
69   | #include "porder.h"
70   | 
71   | 
72   | int retreat_syntaxcheck( char *in_text, int precheck, char *out_string)
73   | {
74   | /*  Read retreat orders in from input file.  */
75   | 
76   |         char c, order;
77   |         int p1, p2, c1, c2;
78   |         char *s;
79   | 	char *out_text = NULL;
80   | 	char temp_out[256];
81   | 	temp_out[0]='\0';
82   | 	s = in_text;
83   |         if (out_string != NULL ) out_text = temp_out;
84   | 
85   | /*  Process lines of the form:
86   | 
87   |  *    cmd ::= <power>: <move list>
88   |  *    move list ::= <move>{; <move list>}
89   |  *    move ::= <type> <province> - <province>
90   |  *           | <type> <province> disband
91   |  *    type ::= Army | Fleet
92   |  */
93   | 
94   |         /* 
95   |          * See if a precheck is possible
96   |          * If so, see if it is an canprocess line and assume ok if so
97   |          */
98   | 
99   |         if (precheck)
100  |         {
101  |                 if (canpreprocess(s))  return 0;
102  |         }
103  | 
104  |         s = get_type(s, &c);
105  |         s = get_prov(s, &p1, &c1);
106  |         AddUnitProvinceToOrder(out_text, c, p1);
107  | 	if (!p1) {
108  |                 errmsg("Unrecognized source province -> %s", *s);
109  |                 return E_WARN;
110  |         }
111  |         s = get_action(s, &order);
112  |         AddOrderToOrder(out_text, order);
113  | 	switch (order) {
114  |         case 'm':
115  |                 s = get_prov(s, &p2, &c2);
116  | 		AddProvinceToOrder(out_text, p2);
117  |                 if (!p2) {
118  |                         errmsg("Movement from %s%s to unrecognized province -> %s",
119  |                                water(p1) ? "the " : "", pr[p1].name, *s);
120  |                         return E_WARN;
121  |                 }
122  |         case 'd':
123  |                 break;
124  | 
125  | 
126  |         default:
127  |                 errmsg("Invalid order for the unit in %s.\n",
128  |                        pr[p1].name);
129  |                 return E_WARN;
130  |         }
131  | 
132  | 	*s = '\0';  /* end of string */
133  | 	if (out_text != NULL) strcat(out_string, out_text);
134  | 	return 0;
135  | }
136  | 
137  | int retreatin(char **s, int pt)
138  | {
139  | 
140  | /*  Read retreat orders in from input file.  */
141  | 
142  | 	char c, order;
143  | 	unsigned char *b;
144  | 	int p1, p2, u, u1, c1, c2, bl;
145  | 
146  | /*  Process lines of the form:
147  | 
148  |  *    cmd ::= <power>: <move list>
149  |  *    move list ::= <move>{; <move list>}
150  |  *    move ::= <type> <province> - <province>
151  |  *           | <type> <province> disband
152  |  *    type ::= Army | Fleet
153  |  */
154  | 
155  | 	*s = get_type(*s, &c);
156  | 	*s = get_prov(*s, &p1, &c1);
157  | 	if (!p1) {
158  | 		errmsg("Unrecognized source province -> %s", *s);
159  | 		return E_WARN;
160  | 	}
161  | 	u1 = -1; /* set to invalid index */
162  | 	for (u = 1; u <= nunit; u++) {
163  | 		if (unit[u].loc == p1 && (unit[u].owner == pt ||
164  | 		    (IS_DUPLEX(dipent) && (unit[u].controller == pt))
165  | 				|| (unit[u].status == 'r' && pt == MASTER))) {
166  | 		    u1 = u;
167  | 		    if (unit[u].status == 'r') {  
168  | 			break;  /* Found a really retreating unit */
169  | 		    }
170  | 		}
171  | 	}
172  | 
173  | 	if (u1 > -1) 
174  | 		u = u1;  /* Restore found retreating unit */
175  | 
176  | 	if (u > nunit) {
177  | 		errmsg("%s doesn't have a unit %s %s.\n", powers[pt],
178  | 		       water(p1) ? "in the" : "in", pr[p1].name);
179  | 		return E_WARN;
180  | 	}
181  | 	if (c != 'x' && c != unit[u].type) {
182  | 		errmsg("The unit %s %s is %s, not %s.\n",
183  | 		       mov_type(p1, u), pr[p1].name,
184  | 		       autype(unit[u].type), autype(c));
185  | 		return E_WARN;
186  | 	}
187  | 	if (unit[u].status != 'r') {
188  | 		errmsg("The %s %s %s was not dislodged.\n",
189  | 		       utype(unit[u].type),
190  | 		       mov_type(p1, u), pr[p1].name);
191  | 		return E_WARN;
192  | 	}
193  | 	*s = get_action(*s, &order);
194  | 	switch (order) {
195  | 	case 'm':
196  | 		*s = get_prov(*s, &p2, &c2);
197  | 		if (!p2) {
198  | 			errmsg("Movement from %s%s to unrecognized province -> %s",
199  | 			       water(p1) ? "the " : "", pr[p1].name, *s);
200  | 			return E_WARN;
201  | 		}
202  | 		if (!valid_move(u, p2, &c2, &bl)) {
203  | 			errmsg("The %s %s %s can't get to %s%s.\n",
204  | 			       utype(unit[u].type),
205  | 			       mov_type(p1, u), pr[p1].name,
206  | 			       water(p2) ? "the " : "", pr[p2].name);
207  | 			return E_WARN;
208  | 		}
209  | 		for (b = unit[u].convoy; b && *b && *b != p2; b++);
210  | 		if (!*b) {
211  | 			errmsg("The %s %s %s can't retreat to %s%s.\n",
212  | 			       utype(unit[u].type),
213  | 			       mov_type(p1, u), pr[p1].name,
214  | 			       water(p2) ? "the " : "", pr[p2].name);
215  | 			return E_WARN;
216  | 		}
217  | 		break;
218  | 
219  | 	case 'd':
220  | 		break;
221  | 
222  | 
223  | 	default:
224  | 		errmsg("Invalid order for the %s %s %s.\n", utype(unit[u].type),
225  | 		       mov_type(p1,u), pr[p1].name);
226  | 		return E_WARN;
227  | 	}
228  | 
229  | 	unit[u].order = order;
230  | 	unit[u].dest = p2;
231  | 	unit[u].dcoast = c2;
232  | 	unit[u].bloc = bl;
233  | 
234  | 	return 0;
235  | 
236  | }
237  | 
238  | void retreatout(int pt)
239  | {
240  | 
241  | 	int p, i, u, u2;
242  | 	char mastrpt_pr[ NPOWER + 1 ];  // Used to be [MAXPLAYERS]. DAN 13/07/99
243  | 	float u_value, u2_value;
244  | 
245  | /*  Generate report  */
246  | 
247  | 	if (err)
248  | 		fprintf(rfp, "\n");
249  | 	fprintf(rfp, "Retreat %s for %s of %d.  (%s.%s)\n\n",
250  | 		 pt ? "orders" : "results",
251  | 		dipent.phase[0] == 'F' ? "Fall" :
252  | 		dipent.phase[0] == 'U' ? "Summer" : "Spring",
253  | 		atoi(&dipent.phase[1]), dipent.name, dipent.seq);
254  | 
255  | 
256  | /*  Pass one, check for conflicts. */
257  | 
258  | 	if (processing || predict) {
259  | 		for (u = 1; u <= nunit; u++) {
260  | 			if (unit[u].status == 'r') {
261  | 				if (unit[u].order == 'm') {
262  | 					if (unit[u].bloc && (u2 = pr[unit[u].bloc].unit) &&
263  | 					    unit[u2].type == 'F') {
264  | 						unit[u].status = 'b';
265  | 					} else if (!IsMultiProvince(unit[u].dest) &&
266  | 						   (u2 = pr[unit[u].dest].unit)) {
267  | 						if (unit[u2].status == 'r')
268  | 							unit[u2].status = 'd';
269  | 						unit[u].status = 'd';
270  | 					} else {
271  | 						pr[unit[u].dest].unit = u;
272  | 					}
273  | 				}
274  | 			}
275  | 		}
276  | 	}
277  | 
278  | /* Pass 1a: undo disbands if unit is bouncing with a unt when a half-value coast */
279  |     for (u = 1; u <= nunit; u++) {
280  |         if (unit[u].status == 'd' && unit[u].order == 'm') {
281  | 	    for (u2 = 1; u2 <= nunit; u2++) {
282  | 	        if (unit[u2].status == 'd' && unit[u2].order == 'm' &&
283  | 		    unit[u].dest == unit[u2].dest && u != u2) {
284  | 		    /* OK, have two units bouncing over same space */
285  | 		    u_value = (unit[u].dcoast == HX || unit[u].dcoast == LX)
286  | 				 ? 0.5 : 1;
287  | 		    u2_value =  (unit[u2].dcoast == HX || unit[u2].dcoast == LX) 
288  | 			    ? 0.5 : 1;
289  | 		    if (u_value > u2_value) 
290  | 			unit[u].status = 'r';  /* cancel bounce */
291  | 		    else if (u2_value > u_value)
292  | 			unit[u2].status = 'r'; /* cancel bounce */
293  | 		}
294  | 	    }
295  | 	}
296  |     }
297  | 		
298  | 
299  | /*  Pass two, display results and move the units.  */
300  | 
301  | 	if (pt == MASTER) {
302  | 		for (u = 0; u <  NPOWER + 1; u++)
303  | 			mastrpt_pr[u] = 0;
304  | 		for (u = 1; u <= nunit; u++) {
305  | 			if (unit[u].owner <= 0)
306  | 				continue;
307  | 			if (mastrpt_pr[unit[u].owner] != 1) {
308  | 				mastrpt_pr[unit[u].owner] = 1;
309  | 				mast_rpt(unit[u].owner, 1);
310  | 			};
311  | 		};
312  | 		fprintf(rfp, "\n");
313  | 	};
314  | 	for (u = 1; u <= nunit; u++) {
315  | 		if (unit[u].status != ':' &&
316  | 		    !gateway(unit[u].loc) && !railway(unit[u].loc) &&
317  | 		    (pt == (p = unit[u].owner) || pt == MASTER || processing ||
318  | 		     (IS_DUPLEX(dipent) && (unit[u].controller == pt)))) {
319  | 			fprintf(rfp, "%s: ", powers[p]);
320  | 			for (i = strlen(powers[p]); i < LPOWER; i++)
321  | 				putc(' ', rfp);
322  | 			fprintf(rfp, "%-5s %s", Utype(unit[u].type), pr[unit[u].loc].name);
323  | 
324  | 			if (unit[u].coast > XC)
325  | 				fprintf(rfp, " (%s)", mtype[unit[u].coast]);
326  | 
327  | 			if (unit[u].order == 'm') {
328  | 				fprintf(rfp, " -> %s", pr[unit[u].dest].name);
329  | 				if (unit[u].dcoast > XC)
330  | 					fprintf(rfp, " (%s)", mtype[unit[u].dcoast]);
331  | 
332  | 				if (unit[u].status == 'd') {
333  | 					fprintf(rfp, " (*bounce, destroyed*)");
334  | 					unit[u].owner = 0;
335  | 
336  | 				} else if (unit[u].status == 'b') {	/* Originates from Machiavelli */
337  | 					fprintf(rfp, " (*blocked, destroyed*)");
338  | 					unit[u].owner = 0;
339  | 
340  | 				} else {
341  | 					unit[u].loc = unit[u].dest;
342  | 					unit[u].coast = unit[u].dcoast;
343  | 				}
344  | 
345  | 			} else if (unit[u].order == 'd') {
346  | 				fprintf(rfp, " DISBAND");
347  | 				unit[u].owner = 0;
348  | 
349  | 			} else {
350  | 				more_orders++;
351  | 				fprintf(rfp, ", No Order Processed (DISBAND)");
352  | 				unit[u].owner = 0;
353  | 			}
354  | 			fprintf(rfp, ".\n");
355  | 
356  | 			unit[u].status = ':';
357  | 
358  | 		}
359  | 	}
360  | 
361  | /* Pass 3: recalculate blockade settings */
362  |         if ((dipent.flags & F_WINGS) && (processing || predict)) {
363  |                 /* Firstly, set all provinces to 'unblockaded' */
364  |                 for (i = 1; i <= npr;i++) {
365  |                         pr[i].blockaded = 0;
366  |                 }
367  |                 /* Now, go through all units and set to blockaded if occupied by an
368  |                    enemy wing
369  |                  */
370  |                  for (u = 1; u <= nunit; u++) {
371  |                         if (unit[u].status == ':' && unit[u].owner != 0 ) {
372  |                                 /* only non-retreating units */
373  |                                 if (unit[u].type == 'W' && unit[u].owner != pr[unit[u].loc].owner) {
374  |                                         /* we can mark non-scs as blockaded, but who cares! */
375  |                                         pr[unit[u].loc].blockaded = 1;
376  |                                 }
377  |                         }
378  |                 }
379  |         }
380  | 
381  | 
382  | }
383  | 
384  | /****************************************************************************/