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 | /****************************************************************************/