1 | /*
2 | * $Log: ma_movement.c,v $
3 | * Revision 1.28 2004/12/28 10:04:40 millis
4 | * Fixed Bug 393 (Mach2 siege break failed)
5 | *
6 | * Revision 1.27 2004/10/23 21:08:59 millis
7 | * Fix Bug 375, Mach2 allow besieged to dislodge
8 | *
9 | * Revision 1.26 2004/05/22 08:58:22 millis
10 | * Bug 297: Add Intimate Diplomacy
11 | *
12 | * Revision 1.25 2003/12/07 00:04:04 millis
13 | * Fix Bug 249, so that disbanding garrisons prevent setting siege state
14 | *
15 | * Revision 1.24 2003/10/08 12:28:08 millis
16 | * Fix bug 236
17 | *
18 | * Revision 1.23 2003/09/14 08:25:13 millis
19 | * Fix bug 225
20 | *
21 | * Revision 1.22 2003/08/09 23:05:52 millis
22 | * Fixed bug 213
23 | *
24 | * Revision 1.21 2003/07/23 22:42:41 millis
25 | * Fix bug 172
26 | *
27 | * Revision 1.20 2003/07/20 15:45:28 millis
28 | * Bug 195 fix
29 | *
30 | * Revision 1.19 2003/05/16 21:13:42 millis
31 | * Bug 155 fix, moved Bug 126 fix to end (as otherwise, does not know of where units were).
32 | *
33 | * Revision 1.18 2003/05/10 00:22:39 millis
34 | * Fix bug 126, not always correctly registering province ownership changes
35 | *
36 | * Revision 1.17 2003/05/03 23:33:49 millis
37 | * Fix bug 150 (NO_GARRISONS flag)
38 | *
39 | * Revision 1.16 2003/01/13 18:19:41 millis
40 | * Merge from ustv
41 | *
42 | * Revision 1.15 2002/12/28 21:25:25 millis
43 | * Fixed typo
44 | *
45 | * Revision 1.14 2002/12/28 00:11:36 millis
46 | * Fixed bug 2, so that disbands can happen even if under siege
47 | *
48 | * Revision 1.13 2002/12/23 01:43:31 millis
49 | * Really fixed Bug 69 (incorrect change)
50 | *
51 | * Revision 1.12 2002/12/22 02:13:25 millis
52 | * Fixed bugs 54, 67 & 69
53 | *
54 | * Revision 1.11 2002/12/04 23:33:14 millis
55 | * Fixed Bug 47 (incorrect ownership change for proxied orders)
56 | *
57 | * Revision 1.10 2002/11/13 22:30:33 millis
58 | * Bug 30, correctly calculate support on assasination
59 | *
60 | * Revision 1.9 2002/10/19 21:39:16 millis
61 | * Fixed Bug 21: Mach2 games allowing Armies in Venice
62 | *
63 | * Revision 1.8 2002/05/14 23:05:49 miller
64 | * Allow signalling convoys in Mach2 games
65 | *
66 | * Revision 1.7 2002/04/22 21:27:40 miller
67 | * Small bug that allowed illegal converts
68 | *
69 | * Revision 1.6 2002/03/05 23:19:49 miller
70 | * Fix special bleagured garisson bug
71 | *
72 | * Revision 1.3.2.1 2001/10/19 23:37:01 dema
73 | * Added NoMoney handling, and correct calculatinos for lifting sieges
74 | *
75 | * Revision 1.3 2001/07/08 22:58:24 miller
76 | * Preliminary check for XF_NOMENY
77 | *
78 | * Revision 1.2 2001/07/01 23:19:29 miller
79 | * Many fixes
80 | *
81 | * Revision 1.1 1998/02/28 17:49:42 david
82 | * Initial revision
83 | *
84 | * Revision 1.1 1996/10/20 12:29:45 rpaar
85 | * Morrolan v9.0
86 | */
87 |
88 | /* po_movechk.c
89 | * Copyright 1987, Lowe.
90 | *
91 | * Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
92 | * Maryland, all rights reserved; used with permission.
93 | *
94 | * Redistribution and use in source and binary forms are permitted
95 | * provided that it is for non-profit purposes, that this and the
96 | * above notices are preserved and that due credit is given to Mr.
97 | * Lowe.
98 | */
99 |
100 | #include <stdlib.h>
101 | #include <string.h>
102 |
103 | #include "dip.h"
104 | #include "porder.h"
105 | #include "mach.h"
106 | #include "functions.h"
107 |
108 | #define convoyable(p) (water(p) | (dipent.xflags & XF_COASTAL_CONVOYS))
109 |
110 | static int result[MAXUNIT];
111 | static int support[MAXUNIT];
112 | static int supportvalue[MAXUNIT];
113 |
114 |
115 | /*
116 | * Input processing of the regular movement orders
117 | */
118 | int ma_movein(char **s, int p)
119 | {
120 | /* char **s; Input stream */
121 | /* int p; Power specification */
122 |
123 | char c, order;
124 | unsigned char *t;
125 | char target_type;
126 | char cc; /* MLM 12/06/2001 remember unit type for convert order */
127 | int i, j, p1, p2, u, u1, u2, c1, c2, bl;
128 | unsigned char *bp;
129 |
130 | /* Process lines of the form:
131 |
132 | * cmd ::= <power>: <move list>
133 | * move list ::= <move>{; <move list>}
134 | * move ::= <type> <province> - <province>
135 | * | <type> <province> holds
136 | * | <type> <province> support {<power>:} <type> <province> {-<province>}
137 | * | <type> <province> convoy {<power>:} <type> <province> {-<province>}
138 | * type ::= Army | Fleet
139 | */
140 |
141 | *s = get_type(*s, &c);
142 | *s = get_prov(*s, &p1, &c1);
143 | if (!p1) {
144 | errmsg("Unrecognized source province -> %s", *s);
145 | return E_WARN;
146 | }
147 | u1 = pr[p1].unit;
148 | u2 = pr[p1].gunit;
149 |
150 | u = c == 'G' ? u2 :
151 | c != 'x' ? u1 :
152 | u1 && unit[u1].owner == p ? u1 :
153 | u2 && unit[u2].owner == p ? u2 :
154 | u1 ? u1 : u2;
155 |
156 | if (!u) {
157 | errmsg("No %s present %s %s.\n", utype(c),
158 | water(p1) ? "in the" : "in", pr[p1].name);
159 | return E_WARN;
160 | }
161 | if (p != unit[u].owner && p != MASTER) {
162 | for (u2 = 1; u2 <= nunit; u2++) {
163 | if (unit[u2].owner == p && unit[u2].proxy == u)
164 | break;
165 | }
166 | if (u2 > nunit) {
167 | if (nunit > MAXUNIT - 2) {
168 | fprintf(rfp, "Too many units in %s.\n", dipent.name);
169 | fprintf(log_fp, "Too many units in %s.\n", dipent.name);
170 | bailout(1);
171 | }
172 | ++nunit;
173 | }
174 | memcpy(&unit[u2], &unit[u], sizeof(unit[0]));
175 | if (u2 == nunit)
176 | unit[nunit].exists = 0; /* Unit does not yet exist */
177 | unit[u2].owner = p;
178 | unit[u2].proxy = u;
179 | unit[u2].order = 'n';
180 | u = u2;
181 | }
182 | if (c != 'x' && c != unit[u].type) {
183 | errmsg("The unit %s %s is %s, not %s.\n",
184 | water(p1) ? "in the" : "in", pr[p1].name,
185 | autype(unit[u].type), autype(c));
186 | return E_WARN;
187 | }
188 | p2 = unit[u].loc;
189 | c2 = unit[u].coast;
190 | u2 = 0;
191 | bp = NULL;
192 | bl = 0;
193 |
194 | *s = get_action(*s, &order);
195 | switch (order) {
196 |
197 | case 'b': /* besiege */
198 | if (unit[u].type == 'G') {
199 | errmsg("Invalid order for garrison.\n");
200 | return E_WARN;
201 | }
202 | if (!has_fortress(p1)) {
203 | errmsg("%s has no fortress to siege.\n", pr[p1].name);
204 | return E_WARN;
205 | }
206 | if (unit[u].type == 'F' && !has_port(p1)) {
207 | errmsg("A fleet can only besiege ports.\n");
208 | return E_WARN;
209 | }
210 | if (!has_crebellion(p1) && pr[p1].gunit == 0) {
211 | errmsg("%s has no garrison or rebellion to siege.\n", pr[p1].name);
212 | return E_WARN;
213 | }
214 | *s = get_type(*s, &c);
215 | if (!strncasecmp(*s, "rebellion", 9))
216 | *s += 9;
217 | break;
218 |
219 | case 'd':
220 | if (!(dipent.xflags & XF_MOVEDISBAND)) {
221 | errmsg("This game does not allow disband orders in movement phases.");
222 | return E_WARN;
223 | }
224 | break;
225 |
226 | case 'c':
227 | case 's':
228 | *s = get_type(*s, &c);
229 | target_type = c; /* Remember type of unit being convoyed/supported */
230 | *s = get_prov(*s, &p2, &c2);
231 | if (!p2) {
232 | errmsg("Unrecognized source province for support/convoy -> %s", *s);
233 | return E_WARN;
234 | }
235 | if (c == 'G') {
236 | u2 = has_garrison(p2);
237 | if (!u2) {
238 | errmsg("No garrison present %s %s.\n",
239 | water(p2) ? "in the" : "in", pr[p2].name);
240 | return E_WARN;
241 | }
242 | } else {
243 | u2 = pr[p2].unit;
244 | if (!u2) {
245 | errmsg("No unit present %s %s.\n",
246 | water(p2) ? "in the" : "in", pr[p2].name);
247 | return E_WARN;
248 | }
249 | }
250 |
251 | if (order == 'c' && unit[u2].type != 'A') {
252 | if (is_garrison(u))
253 | errmsg("Invalid order for garrison.\n");
254 | else
255 | errmsg("The convoy order should specify source %s\n",
256 | "and final destination of an army.");
257 | return E_WARN;
258 | }
259 | if (c != 'x' && c != unit[u2].type) {
260 | errmsg("The unit %s %s is %s, not %s.\n",
261 | water(p2) ? "in the" : "in", pr[p2].name,
262 | autype(unit[u2].type), autype(c));
263 | return E_WARN;
264 | }
265 | t = get_action(*s, &c);
266 |
267 | if (c == 'm') {
268 | *s = get_prov(t, &p2, &c2);
269 | if (!p2) {
270 | errmsg("Support/convoy movement to unrecognized province -> %s",
271 | *s);
272 | return E_WARN;
273 | }
274 | if (pr[p2].flags & PF_VENICE &&
275 | dipent.xflags & XF_MACH2 &&
276 | target_type == 'A') {
277 | errmsg("Armies not allowed in %s.\n", *s);
278 | return E_WARN;
279 | }
280 | }
281 | if (order == 's' && c == 'v') {
282 | if (!is_garrison(u2)) {
283 | errmsg("Can only support conversions from Garrison.\n");
284 | return E_WARN;
285 | }
286 | *s = get_type(t, &c);
287 | }
288 | if (order == 'c' && unit[u].type != 'F') {
289 | errmsg("The %s in %s can't convoy anything!!\n",
290 | utype(unit[u].type), pr[p1].name);
291 | return E_WARN;
292 | }
293 | if (order == 'c' && water(p2)) {
294 | errmsg("The convoy order should specify source %s\n",
295 | "and final destination of an army.");
296 | return E_WARN;
297 | }
298 |
299 | c1 = 0;
300 | if (order == 's' &&
301 | ((is_garrison(u) && p1 != p2) ||
302 | (!is_garrison(u) && (!valid_move(u, p2, &c1, &bl)
303 | || c1 == MX)))) {
304 | errmsg("The %s %s %s can't get to %s%s to support.\n",
305 | utype(unit[u].type),
306 | water(p1) ? "in the" : "in", pr[p1].name,
307 | water(p2) ? "the " : "", pr[p2].name);
308 | return E_WARN;
309 | }
310 | if (dipent.phase[0] == 'F' && pr[p1].type == 'v') {
311 | errmsg("Invalid order for fall in the %s.\n", pr[p1].name);
312 | return E_WARN;
313 | }
314 | if (dipent.phase[0] == 'F' && pr[p2].type == 'v') {
315 | errmsg("Invalid order for fall in the %s.\n", pr[p2].name);
316 | return E_WARN;
317 | }
318 | if (!c2 && unit[u2].type == 'F')
319 | valid_move(u2, p2, &c2, &i); /* set c2 */
320 | break;
321 |
322 |
323 | case 'h':
324 | case 'n':
325 | break;
326 |
327 |
328 | case 'l': /* lift siege */
329 | if (is_garrison(u)) {
330 | errmsg("Invalid order for garrison.\n");
331 | return E_WARN;
332 | }
333 | if (!is_sieged(p1)) {
334 | errmsg("No siege in progress in %s.\n", pr[p1].name);
335 | return E_WARN;
336 | }
337 | if (dipent.xflags & XF_NOLIFT_SIEGE) {
338 | errmsg("No need: any non-besiege order will lift siege in this game.\n");
339 | return E_WARN;
340 | }
341 | break;
342 |
343 |
344 | case 'm':
345 | if (is_garrison(u)) {
346 | errmsg("Invalid order for garrison.\n");
347 | return E_WARN;
348 | }
349 | *s = get_prov(*s, &p2, &c2);
350 | if (!p2) {
351 | errmsg("Movement from %s%s to unrecognized province -> %s",
352 | water(p1) ? "the " : "", pr[p1].name, *s);
353 | return E_WARN;
354 | }
355 | if (pr[p2].flags & PF_VENICE && unit[u].type == 'A' &&
356 | dipent.xflags & XF_MACH2)
357 | {
358 | errmsg("Armies not allowed in %s.\n", *s);
359 | return E_WARN;
360 | }
361 | t = get_action(*s, &c);
362 |
363 | if (c == 'm') {
364 | if (unit[u].type == 'A') {
365 | i = nunit + 1;
366 | unit[i].loc = unit[u].loc;
367 | unit[i].coast = XC;
368 | bp = &heap[hp];
369 | while (c == 'm') {
370 | if (!valid_move(i, p2, &c2, &j) ||
371 | !convoyable(p2) ||
372 | (!(u2 = pr[p2].unit) || unit[u2].type != 'F') ||
373 | (pr[p2].flags & PF_VENICE && dipent.xflags & XF_MACH2)) {
374 | errmsg("The army in %s can't convoy through %s%s.\n",
375 | pr[p1].name, water(p2) ? "the " : "", pr[p2].name);
376 | return E_WARN;
377 | }
378 | if (j && !bl)
379 | bl = j;
380 |
381 | heap[hp++] = pr[p2].unit;
382 | unit[i].loc = p2;
383 |
384 | *s = get_prov(t, &p2, &c2);
385 | if (!p2) {
386 | errmsg("Movement from %s%s to unrecognized province -> %s",
387 | water(p1) ? "the " : "", pr[p1].name, *s);
388 | return E_WARN;
389 | }
390 | t = get_action(*s, &c);
391 | }
392 |
393 | if (!valid_move(i, p2, &c2, &j)) {
394 | errmsg("The army in %s can't convoy from %s to %s.\n",
395 | pr[p1].name, pr[unit[i].loc].name, pr[p2].name);
396 | return E_WARN;
397 | }
398 | if (water(p2)) {
399 | errmsg("The army in %s can't convoy into the %s.\n",
400 | pr[p1].name, pr[p2].name);
401 | return E_WARN;
402 | }
403 | heap[hp++] = 0;
404 | c2 = MV;
405 |
406 | } else {
407 | errmsg("Invalid order syntax for the fleet in %s%s.\n",
408 | water(p1) ? "the " : "", pr[p1].name);
409 | return E_WARN;
410 | }
411 | } else {
412 | if (!valid_move(u, p2, &c2, &bl)) {
413 | errmsg("The %s %s %s can't get to %s%s.\n", utype(unit[u].type),
414 | water(p1) ? "in the" : "in", pr[p1].name,
415 | water(p2) ? "the " : "", pr[p2].name);
416 | return E_WARN;
417 | }
418 | }
419 |
420 | if (dipent.phase[0] == 'F' && pr[p1].type == 'v') {
421 | errmsg("Invalid order for fall in the %s.\n", pr[p1].name);
422 | return E_WARN;
423 | }
424 | if (dipent.phase[0] == 'F' && pr[p2].type == 'v') {
425 | errmsg("Invalid order for fall in the %s.\n", pr[p2].name);
426 | return E_WARN;
427 | }
428 | break;
429 |
430 | case 'p': /* proxy */
431 | *s = get_power(*s, &i);
432 | if (i == 0 || i >= WILD_PLAYER) {
433 | errmsg("Valid power must be specified for proxy order.\n");
434 | return E_WARN;
435 | }
436 | if (!(dipent.x2flags & X2F_PROXY)) {
437 | errmsg("Game %s does not allow proxy orders.\n", dipent.name);
438 | return E_WARN;
439 | }
440 | u2 = i;
441 | break;
442 |
443 |
444 | case 'v': /* convert */
445 | if (dipent.x2flags & X2F_NOGARRISONS) {
446 | errmsg("No garrisons allowed in this game, so conversions are illegal.\n");
447 | return E_WARN;
448 | }
449 | *s = get_type(*s, &cc);
450 | if (is_garrison(u) && cc == 'x') {
451 | if (has_port(p1)) {
452 | errmsg("Conversion must specify army or fleet in port.\n");
453 | return E_WARN;
454 | } else {
455 | cc = 'A';
456 | }
457 | } else if (!is_garrison(u) && cc != 'x' && cc != 'G') {
458 | errmsg("Fleets and armies can only convert to garrisons.\n");
459 | return E_WARN;
460 | }
461 | if (unit[u].type == cc) {
462 | errmsg("The %s in %s already is %s.\n", utype(unit[u].type),
463 | pr[p1].name, autype(cc));
464 | return E_WARN;
465 | }
466 | if (cc == 'x')
467 | cc = 'G';
468 |
469 | if (cc == 'F') {
470 | /* Bug 225, enforce coast on convert to fleet */
471 | *s = get_coast(*s, &c1);
472 | for (t = (char *) pr[unit[u].loc].move; *t; t++)
473 | if (*++t >> 4 == c1)
474 | break;
475 | if (!*t) {
476 | errmsg("Invalid coast specified for fleet in %s.\n",
477 | pr[unit[u].loc].name);
478 | return E_WARN;
479 | }
480 | }
481 |
482 |
483 | if (!has_fortress(p1)) {
484 | errmsg("No fortress or fortified city in %s.\n", pr[p1].name);
485 | return E_WARN;
486 | }
487 | if (cc == 'F' && !has_port(p1)) {
488 | errmsg("%s is not a port. Can only convert to an army.\n",
489 | pr[p1].name);
490 | return E_WARN;
491 | }
492 | if (cc == 'A' &&
493 | pr[p1].flags & PF_VENICE &&
494 | dipent.xflags & XF_MACH2) {
495 | errmsg("Armies not allowed in %s.\n", pr[p1].name);
496 | return E_WARN;
497 | }
498 |
499 | if (c == 'x')
500 | c = unit[u].type;
501 |
502 | /* MLM 12/06/2001 do not allow fleets to convert if not a port */
503 | if (c== 'F' && !has_port(p1)) {
504 | errmsg("%s is not a port. Cannot convert.\n",
505 | pr[p1].name);
506 | return E_WARN;
507 | }
508 | /* MLM 21/6/2001 check if allowed to convert */
509 | if (!PermittedMachUnit(p, cc, unit[u].stype, PP_BUILD)) {
510 | errmsg("Conversion is not permitted for this power.\n");
511 | return E_WARN;
512 | }
513 |
514 | p2 = p1;
515 | u2 = cc;
516 | c2 = cc == 'F' ? c1 : MV;
517 | break;
518 |
519 |
520 | default:
521 | errmsg("Invalid order for the %s %s %s.\n",
522 | utype(unit[u].type),
523 | water(p1) ? "in the" : "in", pr[p1].name);
524 | return E_WARN;
525 | }
526 |
527 | unit[u].order = order;
528 | unit[u].unit = u2;
529 | unit[u].dest = p2;
530 | unit[u].dcoast = c2;
531 | unit[u].convoy = bp;
532 | unit[u].bloc = bl;
533 |
534 | return 0;
535 | }
536 |
537 | #define supval(u) (unit[u].stype == 'm' || unit[u].stype == 'p' ? 2 : 1)
538 |
539 |
540 | /* Function to only cut one support of special units if Mach2 */
541 | void SupportCut(int u, int value)
542 | {
543 | if (dipent.xflags & XF_MACH2) {
544 | support[unit[u].unit]--;
545 | supportvalue[u]--; /* Registers amount of support unit was last giving */
546 | /* See if any support left: if not, mark as cut */
547 | if (!supportvalue[u])
548 | result[u] = value;
549 | } else {
550 | /* Normal game, cuts always full support */
551 | result[u] = value;
552 | support[unit[u].unit] -= supval(u);
553 | }
554 | }
555 |
556 | int ma_moveout(int pt)
557 | {
558 |
559 | /* Process movement orders. */
560 |
561 | int u, u2, u3, u4, bounce = 0, i, index, p, c1;
562 | unsigned char *s, *t, c, contest[NPROV + 1], converted[NPROV+1];
563 | int unit_dislodged;
564 | char cbuffer[1024];
565 |
566 | /* int result[MAXUNIT];
567 | int support[MAXUNIT];
568 | int supportvalue[MAXUNIT];
569 | */ int has_other_retreat = 0;
570 |
571 | int had_rebellion[NPROV+1]; /* Remember if a province had a rebellion */
572 |
573 | #define VOID 1
574 | #define NO_CONVOY 2
575 | #define CUT 3
576 | #define BOUNCE 4
577 | #define NO_SUPPORT 5
578 | #define BESIEGE 6
579 | #define DOSIEGE 7
580 | #define BLOCKED 8
581 | #define BAD_CONVOY 9
582 | #define SELF_BESIEGE 10
583 | #define VENICE_REBEL 11 /* Venice in rebellion fails movement in */
584 | #define DISLODGED 32 /* added on */
585 | #define MAYBE_NO_CONVOY 33 /* no message */
586 |
587 | static char *results[] =
588 | {"dislodged",
589 | "void",
590 | "no convoy",
591 | "cut",
592 | "bounce",
593 | "no support",
594 | "siege in progress",
595 | "siege required",
596 | "blocked",
597 | "WARN: check convoy",
598 | "self-besiege, siege lifted",
599 | "failed, destination in rebellion"};
600 |
601 |
602 | for (p = 1; p <= npr; p++) {
603 | contest[p] = 0;
604 | converted[p] = 0;
605 | had_rebellion[p] = 0;
606 | }
607 |
608 | if (err)
609 | fprintf(rfp, "\n");
610 | fprintf(rfp, "Movement %s for %s of %d.", pt ? "orders" : "results",
611 | dipent.phase[0] == 'F' ? "Fall" :
612 | dipent.phase[0] == 'U' ? "Summer" : "Spring",
613 | atoi(&dipent.phase[1]));
614 |
615 | /* Needed to say which turn no. for mapit */
616 | if (dipent.xflags & XF_NOMONEY)
617 | fprintf(rfp, " (%s.%s)\n", dipent.name, dipent.seq);
618 | else
619 | fputc('\n', rfp);
620 |
621 | /*
622 | ** Flag bogus orders, initialize support.
623 | */
624 |
625 | for (u = 1; u <= nunit; u++) {
626 | result[u] = 0;
627 |
628 | support[u] = unit[u].dcoast == MX ? -1 : supval(u) - 1;
629 | supportvalue[u] = supval(u);
630 |
631 | /*
632 | ** You get an extra support moving/converting into a rebelling province
633 | ** as long as no one else is trying it at the same time.
634 | */
635 |
636 | if ((unit[u].order == 'm' || (unit[u].order == 'v' && unit[u].type == 'G')) &&
637 | pr[p = unit[u].dest].owner != unit[u].owner &&
638 | has_rebellion(p)) {
639 | for (u2 = 1; u2 <= nunit; u2++) {
640 | if (u2 != u &&
641 | unit[u2].owner != pr[p].owner &&
642 | unit[u2].order == 'm' &&
643 | unit[u2].dest == p)
644 | break;
645 | }
646 | if (u2 == nunit + 1)
647 | support[u]++;
648 | }
649 | /*
650 | ** Verify that besiege orders are valid.
651 | */
652 |
653 | p = unit[u].loc;
654 | switch (unit[u].order) {
655 | case 'b': /* BESIEGE ORDER */
656 | if ((!has_garrison(p) && !has_crebellion(p)))
657 | result[u] = VOID;
658 | if ((has_garrison(p) && unit[pr[p].gunit].owner == unit[u].owner))
659 | result[u] = SELF_BESIEGE;
660 | break;
661 | case 'v': /* CONVERT ORDER */
662 | /* A besieged unit can not convert */
663 | if (is_garrison(u)) {
664 | if (is_sieged(p) && unit[pr[p].gunit].owner != unit[u].owner)
665 | result[u] = BESIEGE;
666 | } else {
667 | /*
668 | ** A city with a garrison or rebellion
669 | ** can't be entered
670 | */
671 | if (has_garrison(p) || has_crebellion(p)) {
672 | result[u] = DOSIEGE;
673 | }
674 | }
675 | break;
676 | case 'd':
677 | /* Bug 2, can disband at any time */
678 | break;
679 | default:
680 | /* All other commands */
681 | if (is_sieged(p) && !is_garrison(u) && unit[u].order != 'l'
682 | && unit[pr[p].gunit].owner != unit[u].owner)
683 | if (!(dipent.xflags & XF_NOLIFT_SIEGE))
684 | result[u] = BESIEGE;
685 | }
686 | }
687 |
688 | /*
689 | ** If we're not really processing, clear all the bogus proxy orders and
690 | ** skip to the report generation.
691 | */
692 |
693 | if (!processing && !predict) {
694 | for (u = 1; u <= nunit; u++) {
695 | if (unit[u].proxy != 0 && unit[u].order == 'n')
696 | unit[u].owner = 0;
697 | }
698 | } else {
699 |
700 | /* Pass 0: Substitute all proxy orders. */
701 |
702 | for (p = 0, u = 1; u <= nunit; u++) {
703 | if (unit[u].owner <= 0)
704 | continue;
705 | if (unit[u].order == 'p') {
706 | if (unit[u].owner != p)
707 | fprintf(rfp, "\n");
708 | fprintf(rfp, "%s: %s%s %s", powers[p = unit[u].owner], Stype(unit[u].stype),
709 | Utype(unit[u].type), pr[unit[u].loc].name);
710 | if (unit[u].coast > XC)
711 | fprintf(rfp, " (%s)", mtype[unit[u].coast]);
712 |
713 | fprintf(rfp, " Proxy given to %s.\n", powers[unit[u].unit]);
714 |
715 | unit[u].order = 'n';
716 | for (u2 = 1; u2 <= nunit; u2++) {
717 | if (unit[u2].proxy == u && unit[u2].owner == unit[u].unit) {
718 | unit[u].order = unit[u2].order;
719 | unit[u].unit = unit[u2].unit;
720 | unit[u].dest = unit[u2].dest;
721 | unit[u].dcoast = unit[u2].dcoast;
722 | unit[u].convoy = unit[u2].convoy;
723 | unit[u2].owner = 0;
724 | }
725 | }
726 | }
727 | /*
728 | ** All the proxy orders will be at the end so we can safely delete
729 | ** the extra ones here on a single pass.
730 | */
731 |
732 | if (unit[u].proxy != 0) {
733 | unit[u].owner = 0;
734 | unit[u].order = 'n';
735 | }
736 | }
737 | if (p)
738 | fprintf(rfp, "\n");
739 |
740 |
741 | /* Pass 1: Tally up all the support orders, verify convoys. */
742 |
743 |
744 | for (u = 1; u <= nunit; u++) {
745 | if (unit[u].owner <= 0)
746 | continue;
747 |
748 | if ((p = unit[u].bloc) && (u2 = pr[p].unit) && unit[u2].type == 'F'
749 | && !allies[unit[u2].owner][unit[u].owner]) {
750 | result[u] = BLOCKED;
751 | }
752 | if (unit[u].order == 's' && !result[u]) {
753 | if (unit[u2 = unit[u].unit].order == 'm') {
754 | if (unit[u2].dest == unit[u].dest &&
755 | (unit[u2].dcoast == unit[u].dcoast || unit[u2].dcoast <= XC)) {
756 | if (!result[u2])
757 | support[u2] += supval(u);
758 | } else {
759 | result[u] = VOID;
760 | }
761 | } else {
762 | if (unit[u2].loc == unit[u].dest)
763 | support[u2] += supval(u);
764 | else {
765 | result[u] = VOID;
766 | }
767 | }
768 | } else if (unit[u].order == 'm' && unit[u].convoy != NULL) {
769 | for (s = unit[u].convoy; *s; s++) {
770 | if (unit[*s].order != 'c' || unit[*s].unit != u ||
771 | (unit[*s].dest != unit[u].dest &&
772 | unit[*s].dest != unit[*(s + 1)].loc)) {
773 | result[u] = NO_CONVOY;
774 | support[u] = supval(u) - 1;
775 | break;
776 | }
777 | }
778 | }
779 | }
780 |
781 | /* Pass 2a: Check for support cut from non-convoyed units */
782 |
783 | for (u = 1; u <= nunit; u++) {
784 | if (unit[u].owner <= 0 || result[u])
785 | continue;
786 | if (((unit[u].order == 'm' && !unit[u].convoy && unit[u].dcoast != MX) ||
787 | unit[u].order == 'v')
788 | && (u2 = pr[unit[u].dest].unit)
789 | && unit[u2].order == 's' && !result[u2]
790 | && unit[u2].dest != unit[u].loc /* X */
791 | && unit[u2].owner != unit[u].owner) { /* IX.6.note */
792 | SupportCut(u2,CUT);
793 | }
794 | /* MLM 22/6/2001 Also block moves on attempt to enter
795 | rebelling Venice */
796 | if (unit[u].order == 'm' &&
797 | pr[unit[u].dest].flags & PF_VENICE &&
798 | has_rebellion(unit[u].dest) &&
799 | pr[unit[u].dest].owner == unit[u].owner)
800 | result[u]= VENICE_REBEL;
801 | }
802 |
803 | /* Pass 2a.1: Check for support cut from "support needed" units */
804 |
805 | for (u = 1; u <= nunit; u++) {
806 | if (unit[u].owner <= 0 || result[u])
807 | continue;
808 | if (unit[u].dcoast == MX) {
809 | unit[u].dcoast = MV;
810 | if (unit[u].order == 'm' && !unit[u].convoy && support[u] >= 0
811 | && (u2 = pr[unit[u].dest].unit)
812 | && unit[u2].order == 's' && !result[u2]
813 | && unit[u2].dest != unit[u].loc /* X */
814 | && unit[u2].owner != unit[u].owner) { /* IX.6.note */
815 | SupportCut(u2, CUT);
816 | }
817 | }
818 | }
819 | /* Pass 2a.2: Check again if any partially cut special units are fully cut, only for Mach2 */
820 | if (dipent.xflags & XF_MACH2) {
821 | for (u = 1; u <= nunit; u++) {
822 | /* Only interested in special units */
823 | if (supval(u) < 2) continue;
824 | if (unit[u].order != 's') continue; /* Only want units ordering support */
825 | /* OK, identifier a full cut by an attack with > 0 support */
826 | for (u2 = 1; u2<= nunit; u2++) {
827 | /* Ok, see if a unit is attacking this special unit and,
828 | if the unit attacking is a special too, or has support
829 | it will fully cut the unit[u] support
830 | */
831 | if (unit[u2].order == 'm' &&
832 | unit[u2].dest == unit[u].loc &&
833 | (support[u2] || supval(u2) > 1) &&
834 | unit[u2].owner != unit[u].owner) {
835 | result[u] = CUT;
836 | support[u] -= (supval(u) -1); /* -1 as already reduced by one before */
837 | break;
838 | }
839 | }
840 | }
841 | }
842 |
843 | /* Pass 3a: Check for dislodged convoys. XII.3 */
844 |
845 | for (u = 1; u <= nunit; u++) {
846 | if (unit[u].owner <= 0)
847 | continue;
848 | if (unit[u].order == 'c' && !result[u2 = unit[u].unit]) {
849 | for (s = unit[u2].convoy; s != NULL && *s; s++) {
850 | if (*s == u) {
851 | for (u3 = 1; u3 <= nunit; u3++) {
852 | if (unit[u3].owner <= 0)
853 | continue;
854 | if (unit[u3].order == 'm' && !result[u3] &&
855 | unit[u3].owner != unit[u].owner &&
856 | unit[u3].dest == unit[u].loc && support[u3] > support[u]) {
857 | result[u2] = MAYBE_NO_CONVOY;
858 | goto nextp3a;
859 | }
860 | }
861 | }
862 | }
863 | }
864 | nextp3a:;
865 | }
866 |
867 | /* Pass 2b: Check for support cut from convoyed units */
868 |
869 | for (u = 1; u <= nunit; u++) {
870 | if (unit[u].owner <= 0)
871 | continue;
872 | if (unit[u].order == 'm' && unit[u].convoy && !result[u]
873 | && (u2 = pr[unit[u].dest].unit)
874 | && unit[u2].order == 's' && !result[u2]
875 | && unit[u2].dest != unit[u].loc /* X */
876 | && unit[u2].owner != unit[u].owner) { /* IX.6.note */
877 |
878 | /* XII.5: You can't cut support of attacks against your convoy */
879 |
880 | for (s = unit[u].convoy; s != NULL && *s; s++)
881 | if (unit[u2].unit == *s)
882 | goto nextp2b;
883 |
884 | SupportCut(u2,CUT);
885 | }
886 | nextp2b:;
887 | }
888 |
889 | /* Pass 3b: Recheck for dislodged convoys. XII.3 */
890 |
891 | for (u = 1; u <= nunit; u++) {
892 | if (unit[u].owner <= 0)
893 | continue;
894 | if (unit[u].order == 'c' && result[u2 = unit[u].unit] == MAYBE_NO_CONVOY) {
895 | for (s = unit[u2].convoy; s != NULL && *s; s++) {
896 | if (*s == u) {
897 | for (u3 = 1; u3 <= nunit; u3++) {
898 | if (unit[u3].owner <= 0)
899 | continue;
900 | if (unit[u3].order == 'm' && !result[u3] &&
901 | unit[u3].owner != unit[u].owner &&
902 | unit[u3].dest == unit[u].loc && support[u3] > support[u]) {
903 | result[u] = DISLODGED;
904 | result[u2] = NO_CONVOY;
905 | support[u2] = supval(u2) - 1;
906 | goto nextp3b;
907 | }
908 | }
909 | }
910 | }
911 | }
912 | nextp3b:;
913 | }
914 |
915 | /* Pass 2c: Check for support cut from convoyed units */
916 |
917 | for (u = 1; u <= nunit; u++) {
918 | if (unit[u].owner <= 0)
919 | continue;
920 | if (result[u] == MAYBE_NO_CONVOY) {
921 | result[u] = 0;
922 | if ((u2 = pr[unit[u].dest].unit)
923 | && unit[u2].order == 's' && !result[u2]
924 | && unit[u2].dest != unit[u].loc /* X */
925 | && unit[u2].owner != unit[u].owner) { /* IX.6.note */
926 |
927 | /* XII.5: You can't cut support of attacks against your convoy */
928 |
929 | for (s = unit[u].convoy; s != NULL && *s; s++)
930 | if (unit[u2].unit == *s)
931 | goto nextp2c;
932 |
933 | SupportCut(u2,CUT);
934 | }
935 | }
936 | nextp2c:;
937 | }
938 |
939 | /* Pass 4a: Check for conditional movement blocked by an incoming or converting fleet. */
940 | for (u = 1; u <= nunit; u++) {
941 | if (unit[u].owner <= 0 || result[u])
942 | continue;
943 | if ((p = unit[u].bloc)) {
944 | int j;
945 | i = -1;
946 | u3 = 0;
947 | for (u2 = 1; u2 <= nunit; u2++) {
948 | j = -1;
949 | if (unit[u2].order == 'm' ||
950 | (unit[u2].order == 'v' && unit[u2].type == 'G')) {
951 | if (!result[u2]) {
952 | if (unit[u2].dest == p) {
953 | j = support[u2];
954 | }
955 | } else {
956 | if (unit[u2].loc == p && unit[u2].type != 'G') {
957 | j = supval(u2);
958 | }
959 | }
960 | } else {
961 | if (unit[u2].loc == p && unit[u2].type != 'G') {
962 | j = support[u2];
963 | }
964 | }
965 |
966 | if (j > i) { /* The one with more support makes it? */
967 | i = j;
968 | u3 = u2;
969 | } else if (j == i) { /* If the same, neither makes it */
970 | u3 = 0;
971 | }
972 | }
973 |
974 | if (u3 && ((unit[u3].type == 'F' && !(dipent.xflags & XF_MACH2)) ||
975 | (unit[u3].type == 'G' && unit[u3].unit == 'F')) &&
976 | !allies[unit[u3].owner][unit[u].owner]) {
977 | result[u] = BLOCKED;
978 | if (unit[u].order == 's' && unit[u].unit) {
979 | /* Bug 195
980 | * Blocked units cannot give support, so remove it */
981 | support[unit[u].unit] -= supval(u);
982 | }
983 | }
984 | }
985 | }
986 |
987 | /* Pass 4b: Check for dislodged support. X */
988 |
989 | for (u = 1; u <= nunit; u++) {
990 | if (unit[u].owner <= 0)
991 | continue;
992 | if (unit[u].order == 'm' && (u2 = pr[unit[u].dest].unit)
993 | && unit[u2].order == 's' && !result[u] && !result[u2]
994 | && unit[u2].dest == unit[u].loc) {
995 |
996 | /* Your support cannot dislodge your own unit. IX.3 */
997 |
998 | for (u3 = 1, p = 0; u3 <= nunit; u3++) {
999 | if (unit[u3].owner <= 0)
1000 | continue;
1001 | if (!result[u3] && unit[u3].order == 's' &&
1002 | unit[u3].unit == u && unit[u3].owner == unit[u2].owner)
1003 | p += supval(u3);
1004 | }
1005 |
1006 | if (support[u] - p > support[u2]) {
1007 | for (u4=1, unit_dislodged=1; u4<=nunit; u4++ ) {
1008 | if ( ( u != u4 )
1009 | && ( unit[ u4 ].order == 'm' )
1010 | && ( !result[ u4 ] )
1011 | && ( unit[ u4 ].dest == unit[ u ].dest )
1012 | && ( support[ u4 ] >= support[u]-p ) )
1013 | unit_dislodged = 0;
1014 | }
1015 |
1016 | if ( unit_dislodged == 1 ) {
1017 | result[u2] = DISLODGED;
1018 | support[unit[u2].unit] -= supval(u2);
1019 | }
1020 | }
1021 | }
1022 | }
1023 |
1024 | /* Pass 5aa: Disband requested disband units */
1025 | for (u = 1; u <= nunit; u++) {
1026 | if (unit[u].owner <= 0)
1027 | continue;
1028 | if (unit[u].order == 'd') {
1029 | result[u] = 0; /* Disbands never fail */
1030 | }
1031 | }
1032 |
1033 | /* Pass 5: Check for movement bounces */
1034 |
1035 | do {
1036 | bounce = 0;
1037 | for (u = 1; u <= nunit; u++) {
1038 | if (unit[u].owner <= 0)
1039 | continue;
1040 | if ((unit[u].order == 'm' ||
1041 | (unit[u].order == 'v' && (unit[u].type == 'G' || is_venice(unit[u].loc)))) && !result[u]) {
1042 |
1043 | /*
1044 | ** The destination is contested by unit u unless there is a
1045 | ** unit there moving into unit u's starting location.
1046 | */
1047 |
1048 | /* u2 is set to garrison unit if none in province
1049 | and we're talking about venice */
1050 |
1051 | u2 = pr[unit[u].dest].unit;
1052 | if (!u2 && pr[unit[u].dest].flags & PF_VENICE) {
1053 | u2 = pr[unit[u].dest].gunit;
1054 | }
1055 | if (u2 == u)
1056 | u2 = 0; /* Can't bounce with myself! */
1057 |
1058 | if (!((u2) && !result[u2] /* IX.7.note */
1059 | &&unit[u2].order == 'm'
1060 | && unit[u2].dest == unit[u].loc
1061 | && !unit[u2].convoy && !unit[u].convoy)) { /* XIV.6 */
1062 | contest[unit[u].dest]++;
1063 | }
1064 | p = 0;
1065 | if (u2) {
1066 |
1067 | /*
1068 | ** Your support cannot dislodge your own unit. IX.3
1069 | */
1070 |
1071 | if (result[u2] || (unit[u2].order != 'm' &&
1072 | unit[u2].order != 'v')) {
1073 | for (u3 = 1; u3 <= nunit; u3++) {
1074 | if (unit[u3].owner <= 0)
1075 | continue;
1076 | if (!result[u3] && unit[u3].order == 's' &&
1077 | unit[u3].unit == u && unit[u3].owner == unit[u2].owner)
1078 | p += supval(u3);
1079 | }
1080 | }
1081 | /*
1082 | ** Unit u bounces if the unit there is holding with more support
1083 | ** or trying to go to Venice with a garrison.
1084 | */
1085 |
1086 | if ((unit[u2].order != 'm' && unit[u2].order != 'v' && unit[u2].order != 'd')
1087 | || (unit[u2].dest == unit[u].loc
1088 | && !unit[u2].convoy && !unit[u].convoy)) { /* XIV.6 */
1089 | if (support[u] - p <= support[u2]
1090 | || (unit[u2].type == 'G' && is_venice(unit[u2].loc))
1091 | || unit[u].owner == unit[u2].owner ) { /* IX.3 */
1092 | bounce++;
1093 | result[u] = BOUNCE;
1094 | goto nextp5;
1095 | }
1096 | /*
1097 | ** ...or if the unit's movement bounced and we have no support.
1098 | */
1099 |
1100 | } else if (result[u2]
1101 | && ((support[u] - p < supval(u2))
1102 | || unit[u].owner == unit[u2].owner)) { /* IX.3 */
1103 | bounce++;
1104 | result[u] = BOUNCE;
1105 | goto nextp5;
1106 | }
1107 | }
1108 | /*
1109 | ** Unit bounces if another unit is moving to the same spot with
1110 | ** the same amount or more support.
1111 | */
1112 |
1113 | for (u3 = 1; u3 <= nunit; u3++) {
1114 | if (unit[u3].owner <= 0)
1115 | continue;
1116 | if (u != u3 && unit[u].dest == unit[u3].dest &&
1117 | ((unit[u3].order == 'm' ||
1118 | (unit[u3].order == 'v' && (unit[u3].type == 'G' || is_venice(unit[u3].loc)))) &&
1119 | (!result[u3] || result[u3] == BOUNCE)) &&
1120 | support[u] - p <= support[u3]) {
1121 |
1122 | /*
1123 | ** Won't bounce if unit there dislodges other unit. IX.7.note
1124 | */
1125 |
1126 | if (!(u2 && unit[u2].order == 'm' && !result[u2]
1127 | && unit[u2].dest == unit[u3].loc
1128 | && !unit[u2].convoy && !unit[u3].convoy)) {
1129 | bounce++;
1130 | result[u] = BOUNCE;
1131 | goto nextp5;
1132 | }
1133 | }
1134 | }
1135 | }
1136 | nextp5:;
1137 | }
1138 | } while (bounce);
1139 |
1140 | /* Pass 5a: flag dislodgements */
1141 |
1142 | bounce = 0;
1143 | for (u = 1; u <= nunit; u++) {
1144 | if (unit[u].owner <= 0)
1145 | continue;
1146 | if ((unit[u].order == 'm' ||
1147 | (unit[u].order == 'v' && unit[u].type == 'G')) &&
1148 | !result[u]) {
1149 | if ((u2 = pr[unit[u].dest].unit) &&
1150 | ((unit[u2].order != 'm' && unit[u2].order != 'v' && unit[u2].order != 'd') ||
1151 | result[u2]) &&
1152 | (dipent.xflags & XF_MACH2 || !(unit[u2].type == 'G' && is_sieged(unit[u2].loc)))) {
1153 | /* fprintf(rfp, "First part: Dislodge in %s unit %d by %d\n",
1154 | */
1155 | /* pr[unit[u2].loc].name, u2, u); */
1156 | if (result[u2] < DISLODGED)
1157 | result[u2] += DISLODGED;
1158 | unit[u2].status = 'r';
1159 | bounce++;
1160 | }
1161 | } else if (unit[u].order == 'b' && !result[u]) {
1162 | if ((u2 = has_garrison(unit[u].loc))) {
1163 | for (u3 = 1; u3 <= nunit; u3++) {
1164 | if (unit[u3].order == 'm'
1165 | && unit[u3].dest == unit[u].loc
1166 | && !result[u3]) {
1167 | break;
1168 | }
1169 | }
1170 | if (u3 > nunit) {
1171 | /* fprintf(rfp, "Second part: Dislodge in %s unit %d by %d\n",
1172 | */
1173 | /* pr[unit[u2].loc].name, u2, u); */
1174 | if (is_sieged(unit[u].loc)) {
1175 | if (!(dipent.xflags & XF_BESEIGED_CAN_DISLODGE) ||
1176 | (support[u] >= support[u2])) {
1177 | if (result[u2] < DISLODGED)
1178 | result[u2] += DISLODGED;
1179 | unit[u2].status = 'r';
1180 | bounce++;
1181 | }
1182 | } else {
1183 | if (!result[u2] && unit[u2].order != 'd' &&
1184 | !(dipent.xflags & XF_BESEIGED_CAN_DISLODGE))
1185 | result[u2] = BESIEGE; /* Bug2, must not be disbanding */
1186 | /* Bug375, Mach2 cannot prevent besieger dislodging */
1187 | }
1188 | }
1189 | }
1190 | }
1191 | }
1192 | /* fprintf(rfp, "%s : %d\n", pr[unit[1].loc].name, result[1]); */
1193 | }
1194 |
1195 | /* Pass 6: Process movement, print report */
1196 |
1197 | for (p = 0, u = 1; u <= nunit; u++) {
1198 | if (unit[u].owner <= 0)
1199 | continue;
1200 | if (processing || pt == unit[u].owner || pt == MASTER) {
1201 | if (unit[u].owner != p)
1202 | fprintf(rfp, "\n");
1203 | if ((unit[u].owner != p) && (pt == MASTER)) {
1204 | mast_rpt(unit[u].owner, 0);
1205 | };
1206 |
1207 | fprintf(rfp, "%s: ", powers[p = unit[u].owner]);
1208 |
1209 | if (unit[u].proxy != 0)
1210 | fprintf(rfp, "%s ", owners[unit[unit[u].proxy].owner]);
1211 |
1212 | fprintf(rfp, "%s%s %s", Stype(unit[u].stype),
1213 | Utype(unit[u].type), pr[unit[u].loc].name);
1214 | if (unit[u].coast > XC)
1215 | fprintf(rfp, " (%s)", mtype[unit[u].coast]);
1216 |
1217 | switch (unit[u].order) {
1218 | case 'b':
1219 | fprintf(rfp, " BESIEGE");
1220 | if ((u2 = has_garrison(unit[u].loc))) {
1221 | fprintf(rfp, " %s %s", owners[unit[u2].owner], Utype(unit[u2].type));
1222 | } else if (has_crebellion(unit[u].loc)) {
1223 | fprintf(rfp, " Rebellion");
1224 | }
1225 | break;
1226 |
1227 | case 'c':
1228 | fprintf(rfp, " CONVOY ");
1229 | if ((i = unit[u2 = unit[u].unit].owner) != p)
1230 | fprintf(rfp, "%s ", owners[i]);
1231 | else if (!processing && !predict && unit[u2].dest != unit[u].dest)
1232 | result[u] = BAD_CONVOY;
1233 | fprintf(rfp, "Army %s -> %s", pr[unit[u2].loc].name,
1234 | pr[unit[u].dest].name);
1235 | break;
1236 |
1237 | case 'h':
1238 | fprintf(rfp, " HOLD");
1239 | break;
1240 |
1241 | case 'd':
1242 | fprintf(rfp, " DISBAND");
1243 | break;
1244 |
1245 | case 'l':
1246 | fprintf(rfp, " LIFT SIEGE");
1247 | break;
1248 |
1249 | case 'm':
1250 | if ((s = unit[u].convoy)) {
1251 | while (*s) {
1252 | if (!processing && !predict && unit[*s].owner == unit[u].owner &&
1253 | (unit[*s].order != 'c' || unit[*s].unit != u ||
1254 | unit[*s].dest != unit[u].dest)) {
1255 | result[u] = BAD_CONVOY;
1256 | }
1257 | fprintf(rfp, " -> %s", pr[unit[*s++].loc].name);
1258 | }
1259 | }
1260 | fprintf(rfp, " -> %s", pr[unit[u].dest].name);
1261 | if (unit[u].dcoast > XC)
1262 | fprintf(rfp, " (%s)", mtype[unit[u].dcoast]);
1263 | break;
1264 |
1265 | case 'n':
1266 | if (unit[u].owner != AUTONOMOUS) {
1267 | fprintf(rfp, ", No Order Processed");
1268 | if (dipent.phase[0] != 'F' || pr[unit[u].loc].type != 'v')
1269 | more_orders++;
1270 | }
1271 | break;
1272 |
1273 | case 'p':
1274 | fprintf(rfp, " PROXY TO %s", powers[unit[u].unit]);
1275 | break;
1276 |
1277 | case 's':
1278 | fprintf(rfp, " SUPPORT ");
1279 | if ((i = unit[u2 = unit[u].unit].owner) != p)
1280 | fprintf(rfp, "%s ", owners[i]);
1281 | fprintf(rfp, "%s %s", Utype(unit[u2].type), pr[unit[u2].loc].name);
1282 | if (unit[u2].coast > XC)
1283 | fprintf(rfp, " (%s)", mtype[unit[u2].coast]);
1284 |
1285 | if (unit[u2].loc != unit[u].dest) {
1286 | fprintf(rfp, " -> %s", pr[unit[u].dest].name);
1287 | if (unit[u].dcoast > XC)
1288 | fprintf(rfp, " (%s)", mtype[unit[u].dcoast]);
1289 | }
1290 | if (unit[u2].type == 'G') {
1291 | fprintf(rfp, " CONVERSION");
1292 | }
1293 | break;
1294 |
1295 | case 'v':
1296 | fprintf(rfp, " CONVERT TO %s", Utype(unit[u].unit));
1297 | if ((c1 = unit[u].dcoast) > XC)
1298 | fprintf(rfp, " (%s)", mtype[c1]);
1299 |
1300 | break;
1301 |
1302 | default:
1303 | fprintf(rfp, " INVALID ORDER (internal error)");
1304 | }
1305 |
1306 | if (result[u]) {
1307 | fprintf(rfp, ". (*%s%s*)\n", results[result[u] % DISLODGED],
1308 | result[u] > DISLODGED ? ", dislodged" : "");
1309 | } else {
1310 | fprintf(rfp, ".\n");
1311 | }
1312 | }
1313 | }
1314 |
1315 | /* Pass 7: Print out retreats. */
1316 |
1317 | if (processing || predict) {
1318 | i = 0;
1319 | for (u = 1; u <= nunit; u++) {
1320 | /* Bug 249, reset disbanding owners to zero */
1321 | if (unit[u].owner > 0 && unit[u].order == 'd')
1322 | unit[u].owner = 0;
1323 | }
1324 |
1325 | for (u = 1; u <= nunit; u++) {
1326 | if (unit[u].owner <= 0)
1327 | continue;
1328 |
1329 | /*
1330 | ** Advance moving unit into the destination province.
1331 | */
1332 |
1333 | if (unit[u].order == 'm' && !result[u]) {
1334 | unit[u].loc = p = unit[u].dest;
1335 | unit[u].coast = unit[u].dcoast;
1336 | /*
1337 | ** Moving into anothers power province which is in rebellion or
1338 | ** which city is in rebellion liberates the rebellion.
1339 | */
1340 | if (has_rebellion(p) &&
1341 | pr[p].owner != unit[u].owner) {
1342 | if (!i++)
1343 | fprintf(rfp, "\n");
1344 | fprintf(rfp, "Rebellion in %s liberated.\n", pr[p].name);
1345 | remove_rebellion(p);
1346 | had_rebellion[p] = 1; /* Remember had a rebellion */
1347 | }
1348 | }
1349 | /*
1350 | ** Holding in a province will put down a rebellion.
1351 | */
1352 |
1353 | if (unit[u].order == 'h' && !result[u]) {
1354 | if (has_prebellion(p = unit[u].loc)) {
1355 | if (!i++)
1356 | fprintf(rfp, "\n");
1357 | fprintf(rfp, "Rebellion in %s put down.\n", pr[p].name);
1358 | remove_prebellion(p);
1359 | }
1360 | }
1361 | /*
1362 | ** Change the type of units who are converting.
1363 | */
1364 |
1365 | else if (unit[u].order == 'v' && !result[u]) {
1366 | unit[u].type = unit[u].unit;
1367 | unit[u].coast = unit[u].dcoast;
1368 | p = unit[u].loc;
1369 | /*
1370 | ** Adjust the gunit flag
1371 | */
1372 | if (is_garrison(u))
1373 | pr[unit[u].loc].gunit = u;
1374 | if (pr[unit[u].loc].unit == u)
1375 | pr[unit[u].loc].unit = 0; /* If I converted, mark my province as empty */
1376 | else
1377 | pr[unit[u].loc].gunit = 0;
1378 | converted[unit[u].loc] = u; /* remember that a conversion occured */
1379 |
1380 | if (has_rebellion(p) &&
1381 | pr[p].owner != unit[u].owner) {
1382 | if (!i++)
1383 | fprintf(rfp, "\n");
1384 | fprintf(rfp, "Rebellion in %s liberated.\n", pr[p].name);
1385 | remove_rebellion(p);
1386 | }
1387 | }
1388 | /*
1389 | ** A besiege sets the flag, or if already set, clears it.
1390 | */
1391 |
1392 | else if (unit[u].order == 'b' && !result[u]) {
1393 | if (is_sieged(p = unit[u].loc)) {
1394 | if (has_crebellion(p)) {
1395 | if (!i++)
1396 | fprintf(rfp, "\n");
1397 | fprintf(rfp, "Rebellion in %s put down.\n", pr[p].name);
1398 | }
1399 | remove_siege(p);
1400 | remove_rebellion(p);
1401 | } else {
1402 | /* Bug 249, only set siege if garrison is not disbanding */
1403 | if (!(pr[p].gunit != 0 && unit[pr[p].gunit].order == 'd')) {
1404 | set_siege(p);
1405 | }
1406 | }
1407 | }
1408 | /*
1409 | ** Clear the siege in process flag if lifted.
1410 | */
1411 |
1412 | else if ((unit[u].order == 'l' && !result[u]) ||
1413 | (result[u] == SELF_BESIEGE) ||
1414 | (unit[u].type != 'G' && (dipent.xflags & XF_NOLIFT_SIEGE) )) {
1415 | remove_siege(unit[u].loc);
1416 | }
1417 | }
1418 |
1419 | if (bounce)
1420 | fprintf(rfp, "\n\nThe following units were dislodged:\n\n");
1421 | bounce = 0;
1422 |
1423 | for (u = 1; u <= nunit; u++) {
1424 | if (unit[u].owner <= 0)
1425 | continue;
1426 | if (unit[u].status == 'r') {
1427 | unsigned char buffer[1024];
1428 | unit[u].convoy = &heap[hp];
1429 | t = buffer;
1430 | sprintf(t, "The %s %s in %s%s", owners[unit[u].owner],
1431 | Utype(unit[u].type),
1432 | water(unit[u].loc) ? "the " : "",
1433 | pr[unit[u].loc].name);
1434 | remove_siege(unit[u].loc);
1435 | while (*t)
1436 | t++;
1437 | if (unit[u].coast > XC) {
1438 | sprintf(t, " (%s)", mtype[unit[u].coast]);
1439 | while (*t)
1440 | t++;
1441 | }
1442 | i = 0;
1443 | has_other_retreat = 0;
1444 | if (!is_garrison(u) && !(dipent.xflags & XF_AUTODISBAND)) {
1445 | for (s = pr[unit[u].loc].move; (p = *s++); s++) {
1446 | if (!contest[p] && (((unsigned char) *s) >> 4) == unit[u].coast && (*s & 0x0f) != MX
1447 | && (!(u2 = pr[p].unit) /* XI: can't retreat to */
1448 | ||(unit[u2].loc != p /* attackers origin. */
1449 | && unit[u2].loc != unit[u].loc))
1450 | && (dipent.phase[0] != 'F' || pr[p].type != 'v')) {
1451 | has_other_retreat = 1;
1452 |
1453 | if (!i++)
1454 | sprintf(t, " can retreat to ");
1455 | else
1456 | sprintf(t, " or ");
1457 | while (*t)
1458 | t++;
1459 |
1460 | heap[hp++] = p;
1461 | sprintf(t, "%s", pr[p].name);
1462 | while (*t)
1463 | t++;
1464 | if ((c = *s & 0x0f) > XC) {
1465 | sprintf(t, " (%s)", mtype[c]);
1466 | while (*t)
1467 | t++;
1468 | cbuffer[i - 1] = c;
1469 | } else
1470 | cbuffer[i - 1] = 0;
1471 | bounce++;
1472 | }
1473 | }
1474 |
1475 | /*
1476 | ** The rules to retreat to a garrison are:
1477 | ** 1a) There was no conversion FROM garrison made
1478 | ** 1) There should be a fortress.
1479 | ** 2) It should be unoccupied.
1480 | ** 3) Free of rebellion.
1481 | ** 3aa) Didn't have a rebellion against unit's onwer
1482 | ** 3a) not Venice
1483 | ** 4) And if we are a fleet, it should have a port.
1484 | */
1485 | if (!converted[unit[u].loc] &&
1486 | has_fortress(unit[u].loc) &&
1487 | !has_garrison(unit[u].loc) &&
1488 | !has_crebellion(unit[u].loc) &&
1489 | !(had_rebellion[unit[u].loc] && pr[unit[u].loc].owner == unit[u].owner) &&
1490 | !is_venice(unit[u].loc) &&
1491 | (has_port(unit[u].loc) || unit[u].type == 'A')) {
1492 | if (!i++ || dipent.xflags & XF_GCONVERT_ANYTIME) {
1493 | sprintf(t, "%s can convert to a Garrison",
1494 | has_other_retreat ? " or" : "");
1495 | while (*t)
1496 | t++;
1497 | heap[hp++] = unit[u].loc;
1498 | bounce++;
1499 | }
1500 | }
1501 | } else {
1502 | /*
1503 | ** The garrison was eliminated.
1504 | */
1505 | pr[unit[u].loc].gunit = 0;
1506 | }
1507 |
1508 | if (!i) {
1509 | sprintf(t, " with no valid retreats was destroyed.\n");
1510 | unit[u].owner = 0;
1511 | } else {
1512 | sprintf(t, ".\n");
1513 | }
1514 | heap[hp++] = 0;
1515 |
1516 | /* put the coasts on the heap */
1517 | unit[u].rcoast = &heap[hp];
1518 | for (index = 0; index < i; index++)
1519 | heap[hp++] = cbuffer[index];
1520 | heap[hp++] = 0;
1521 |
1522 | for (t = s = buffer, p = 0; *s; s++, p++) {
1523 | if (p > 78) {
1524 | while (*--s != ' ');
1525 | *s++ = '\0';
1526 | fprintf(rfp, "%s\n", t);
1527 | t = s;
1528 | p = 0;
1529 | }
1530 | }
1531 | fprintf(rfp, "%s", t);
1532 | }
1533 | }
1534 | }
1535 |
1536 | /* Bug 126, correct the units in province, setting to zero if unit is not really there */
1537 | if (processing)
1538 | for (p = 1; p <= npr; p++)
1539 | if (unit[pr[p].unit].loc != p)
1540 | pr[p].unit = 0;
1541 | if (unit[pr[p].gunit].loc != p)
1542 | pr[p].gunit = 0;
1543 |
1544 | return bounce; /* return true if retreats are needed */
1545 | }