1 | /*
2 | ** $Log: st_build.c,v $
3 | ** Revision 1.33 2004/10/05 23:18:11 millis
4 | ** Fix Bug 366 (cannot waive in duplex games)
5 | **
6 | ** Revision 1.32 2004/07/25 16:13:44 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.31 2004/07/13 00:29:26 millis
12 | ** Fix bug 339, allowing build transform games to permit players changing
13 | ** mind about builds without allowing too many.
14 | **
15 | ** Revision 1.30 2004/06/27 01:50:22 millis
16 | ** Futher Intimate fixes (Bug 297) specifically to allow phased orders
17 | ** and correct turns not processing, plus more information printed.
18 | **
19 | ** Revision 1.29 2004/05/23 22:48:12 millis
20 | ** Fix for Intimate, show default disbands for controlled powers
21 | **
22 | ** Revision 1.28 2004/05/22 08:51:02 millis
23 | ** Bug 297: Add Intimate Diplomacy
24 | **
25 | ** Revision 1.27 2004/01/04 11:34:35 millis
26 | ** Implement Bug #262 (ExtraCentres for 1900 Steamroller)
27 | **
28 | ** Revision 1.26 2003/07/24 23:00:43 millis
29 | ** Fix bug 202
30 | **
31 | ** Revision 1.25 2003/05/14 07:59:24 millis
32 | ** Antoher build problem resolved (all builds were rejected).
33 | **
34 | ** Revision 1.24 2003/05/12 21:43:04 millis
35 | ** Fixed bug that didn't work out correct builds.
36 | **
37 | ** Revision 1.23 2003/05/12 02:38:23 millis
38 | ** Bug 118 fix, correctly handle transforms for fleets.
39 | **
40 | ** Revision 1.22 2003/05/10 00:46:15 millis
41 | ** Bug 140 fix, display 'orders' when orders and 'results' when results
42 | **
43 | ** Revision 1.21 2003/05/04 22:39:45 millis
44 | ** Fixed build counting in ExtraCentres() game
45 | **
46 | ** Revision 1.20 2003/05/03 22:45:32 millis
47 | ** take account of ExtraCentres() if set.
48 | **
49 | ** Revision 1.19 2003/05/02 21:39:33 millis
50 | ** Added handling of assumed home centres
51 | **
52 | ** Revision 1.18 2003/02/09 23:07:09 millis
53 | ** Corrected order of GetUnit() call
54 | ** Also removed superfluous blank line on order output
55 | **
56 | ** Revision 1.17 2003/02/05 01:10:00 millis
57 | ** Fixed small bug with pending build unit count
58 | **
59 | ** Revision 1.16 2003/01/18 23:46:03 millis
60 | ** Integrated USTV changes.
61 | **
62 | ** Revision 1.15 2003/01/18 15:17:28 millis
63 | ** first intermediate checkin, with Native and Multi-province support
64 | **
65 | ** Revision 1.14 2002/12/12 01:46:31 millis
66 | ** Fixed Bug 51 (Suez counting as an owned province)
67 | **
68 | ** Revision 1.13 2002/06/10 22:07:02 millis
69 | ** Small fix to init_build(), that was counting gateway units as units
70 | **
71 | ** Revision 1.12 2002/05/17 11:34:26 miller
72 | ** Fixed small bug for CheckOwnedOK() returning bad value
73 | **
74 | ** Revision 1.11 2002/05/11 09:15:34 greg
75 | ** Minor bug fixes
76 | ** - fixed subjectline for absence requests
77 | ** - fixed phase length, so it's no longer hard coded for responses
78 | ** - partial fix for unusable builds, players with only unusable builds
79 | ** will no longer be flagged as having orders due, however players
80 | ** with some usable builds will need to waive any unusable builds,
81 | ** also, if one or more players have unusable builds, but no
82 | ** player has usable builds, the build phase will process after
83 | ** a short delay
84 | **
85 | ** Revision 1.10 2002/04/15 12:55:46 miller
86 | ** Multiple changes for blind & Colonial & setup from USTV
87 | **
88 | ** Revision 1.9 2001/12/28 06:21:24 nzmb
89 | ** Fixed bug causing segmentation fault in st_build.c .. there was an extra %s in line 402.
90 | **
91 | ** Revision 1.7 2001/10/29 23:41:34 miller
92 | ** Fixed reoccurring chaos bug (use centre() macro not 'x' to test province)
93 | **
94 | ** Revision 1.6 2001/10/20 12:11:14 miller
95 | ** Merged in changes from DEMA and USTV
96 | **
97 | ** Revision 1.5.2.1 2001/10/20 00:52:49 dedo
98 | ** Remvoe compile warnings
99 | **
100 | ** Revision 1.5 2001/02/26 11:27:12 miller
101 | ** Fixed small bug on line 107, using [i] instead of [p]
102 | **
103 | ** Revision 1.4 2000/11/18 21:12:14 miller
104 | ** Fixed ONECENTRE bug
105 | **
106 | ** Revision 1.3 2000/11/16 20:42:40 miller
107 | ** New changes for phased move syntax check, transformations and onecentre/anycentre
108 | **
109 | ** Revision 1.2 1999/02/04 19:10:52 davidn
110 | ** buildout(). Altered index of mastrpt_pr to fix crashes on USTR.
111 | ** Array is indexed by unit[].owner, which can be 1 to NPOWER (=around 100 on the
112 | ** standard judge). MAXPLAYER is 50.
113 | **
114 | ** Revision 1.1 1998/02/28 17:49:42 david
115 | ** Initial revision
116 | **
117 | ** Revision 1.1 1996/10/20 12:29:45 rpaar
118 | ** Morrolan v9.0
119 | **
120 | */
121 |
122 | /* st_build.c
123 | **
124 | ** Copyright 1987, Lowe.
125 | **
126 | ** Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
127 | ** Maryland, all rights reserved; used with permission.
128 | **
129 | ** Redistribution and use in source and binary forms are permitted
130 | ** provided that it is for non-profit purposes, that this and the
131 | ** above notices are preserved and that due credit is given to Mr.
132 | ** Lowe.
133 | **
134 | */
135 |
136 | /*
137 | * 03 Dec 1999 Millis Miller Discount blockaded centres from powers builds
138 | */
139 |
140 | #include <stdio.h>
141 | #include <stdlib.h>
142 | #include <string.h>
143 |
144 | #include "dip.h"
145 | #include "functions.h"
146 | #include "porder.h"
147 |
148 | #define PossibleHomeCentre(p1) (p1 == 'x' || \
149 | (p1 >= '0' && p1 <= '9') || \
150 | (p1 >= 'A' && p1 <= 'Z'))
151 |
152 |
153 | int one_owned[NPOWER + 1];
154 | static int ExtraYearCentres(void);
155 |
156 | int ExtraCentres(int power)
157 | {
158 | int ret_val = ExtraYearCentres();
159 | int i;
160 | int found = 0;
161 |
162 | /* As can be called externally, make sure one_owned is correct */
163 | one_owned[power] = 0;
164 | for (i = 1; i <= npr; i++) {
165 | if (pr[i].owner == power &&
166 | ((pr[i].type == dipent.pl[power]) || dipent.xflags & XF_BUILD_ANYCENTRES) &&
167 | pr[i].blockaded == 0 ) { /* Was doing something here, no more! */
168 | if (pr[i].owner == power && pr[i].type == dipent.pl[power]) {
169 | one_owned[power]++;
170 | }
171 | }
172 | }
173 |
174 | if (dipent.x2flags & X2F_EXTRA_HC) {
175 | for (i = 0; i < MAXPLAYERS &&
176 | !found &&
177 | extra_centres[i].power_letter != '\0'; i++) {
178 | if (pletter[dipent.variant][power] == extra_centres[i].power_letter) {
179 | found++;
180 | if (one_owned[power])
181 | ret_val += extra_centres[i].count;
182 | }
183 |
184 | }
185 | }
186 |
187 | return ret_val;
188 | }
189 |
190 | static int ExtraYearCentres(void)
191 | {
192 | int current_year;
193 | int first_year;
194 |
195 | if (dipent.extra_centres == 0)
196 | return 0;
197 |
198 | current_year = atoi(&dipent.phase[1]);
199 | first_year = atoi(&sphase[dipent.variant][1]);
200 |
201 | if ((current_year - first_year) <= dipent.extra_centres)
202 | return (dipent.extra_centres - current_year + first_year);
203 |
204 | return 0;
205 | }
206 |
207 |
208 |
209 | int CountHomeCentres( int p)
210 | {
211 | int total = 0;
212 | int i;
213 |
214 | for ( i = 1; i <= npr; i++)
215 | if (pr[i].type == pletter[dipent.variant][p] )
216 | total++;
217 |
218 | return total;
219 | }
220 |
221 | int CountCentres( int p )
222 | {
223 |
224 | int total = 0;
225 | int i;
226 |
227 | for ( i = 1; i <= npr; i++)
228 | if (pr[i].owner == p )
229 | total++;
230 |
231 | return total;
232 | }
233 |
234 |
235 | static int nu[NPOWER + 1], lu[NPOWER + 1];
236 | /* See if passed location is in conditions to be built on */
237 | int CheckOwnedOK( char type, int u, int p, int p1, int *c1)
238 | {
239 | unsigned char *t;
240 |
241 | if (pr[p1].blockaded) {
242 | errmsg("Cannot transform wing as it is blockading %s.\n",
243 | pr[p1].name);
244 | return 0;
245 | }
246 |
247 | if (water(p1)) {
248 | errmsg("Cannot transform %s while %s water.\n",
249 | utype(unit[u].type),
250 | unit[u].type == 'F' ? "in" : "over");
251 | return 0;
252 | }
253 |
254 | if (type == 'x') {
255 | errmsg("Unit type must be specified.\n");
256 | return 0;
257 | }
258 | if (type == 'A')
259 | *c1 = MV;
260 | else if (type == 'W' ) {
261 | *c1 = MV; /* Temporary for Wings */
262 | if (!(dipent.flags & F_WINGS)) {
263 | errmsg("This game does not allow wings.\n");
264 | return 0;
265 | }
266 | } else if (type == 'R') {
267 | *c1 = MV;
268 | if (!(dipent.x2flags & X2F_ARTILLERY)) {
269 | errmsg("This game does not allow artillery.\n");
270 | return 0;
271 | }
272 | } else {
273 | if (!(*c1))
274 | *c1 = XC;
275 | for (t = (char *) pr[p1].move; *t; t++)
276 | if (*++t >> 4 == *c1)
277 | break;
278 | if (!*t) {
279 | errmsg("Invalid coast specified for fleet in %s.\n",
280 | pr[p1].name);
281 | return 0;
282 | }
283 | }
284 |
285 | return 1; /* it passed ok */
286 | }
287 | /****************************************************************************/
288 | void init_build(void)
289 | {
290 | int i=0, p;
291 |
292 |
293 | UpdateBlockades();
294 |
295 | /* Count number of units allowed to be built. */
296 |
297 | for (p = 1; p <= NPOWER; p++) {
298 |
299 | /* Just in case it got changed, i.e. X2F_MORE_HOMES game! */
300 | if (PossibleHomeCentre(pr[p].type) &&
301 | PossibleHomeCentre(pletter[dipent.variant][pr[p].home]))
302 | pr[p].type = pletter[dipent.variant][pr[p].home];
303 |
304 | nu[p] = 0;
305 | lu[p] = 0;
306 |
307 | nu[p] += ExtraCentres(p);
308 |
309 | for (i = 1; i <= npr; i++) {
310 | if (gateway(i)) continue; /* Gateways don't count */
311 | /* Just in case it got changed, i.e. X2F_MORE_HOMES game! */
312 | if (PossibleHomeCentre(pr[i].type) &&
313 | PossibleHomeCentre(pletter[dipent.variant][pr[i].home]))
314 | pr[i].type = pletter[dipent.variant][pr[i].home];
315 |
316 |
317 | if (pr[i].owner == p &&
318 | pr[i].blockaded == 0 &&
319 | PossibleHomeCentre(pr[i].type))
320 | nu[p]++;
321 | }
322 |
323 | for (i = 1; i <= nunit; i++)
324 | if (unit[i].owner == p && !gateway(unit[i].loc))
325 | nu[p]--;
326 | if (nu[p] < 0)
327 | nu[p]--;
328 | if (nu[p] > 0)
329 | nu[p]++;
330 |
331 | }
332 | }
333 |
334 | int build_syntaxcheck(char *in_text, int precheck, char *out_string)
335 | {
336 | char *s = in_text;
337 |
338 | char type, order;
339 | unsigned char *t;
340 | int p1, c1;
341 | char temp_out[256];
342 | char *out_text = NULL;
343 | temp_out[0]='\0';
344 | if (out_string != NULL) out_text = temp_out;
345 |
346 | /*
347 | ** Process lines of the form:
348 | **
349 | ** cmd ::= <power>: {<order>} <type> <province>
350 | ** order ::= build | remove ¦ transform
351 | ** type ::= Army | Fleet | Wing
352 | */
353 |
354 | /*
355 | * See if a precheck is possible
356 | * If so, see if it is an canprocess line and assume ok if so
357 | */
358 |
359 | if (precheck)
360 | {
361 | if (canpreprocess(s)) return 0;
362 | }
363 |
364 | s = get_order(s, &order);
365 | AddOrderToOrder(out_text,order);
366 | s = get_type(s, &type);
367 | s = get_prov(s, &p1, &c1);
368 |
369 | if (!p1 && order != 'w') {
370 | errmsg("Unrecognized province -> %s", s);
371 | return E_WARN;
372 | }
373 | *s = '\0'; /* End of string found, so terminate it */
374 |
375 | switch (order) {
376 | case 'b':
377 | AddUnitProvinceToOrder(out_text,type, p1);
378 | if (type == 'x') {
379 | errmsg("Unit type must be specified for build.\n");
380 | return E_WARN;
381 | }
382 | if (pr[p1].type == 'w' || pr[p1].type == 'l') {
383 | errmsg("%s is not a supply centre.\n",
384 | pr[p1].name);
385 | return E_WARN;
386 | }
387 |
388 | if (type == 'A')
389 | c1 = MV;
390 | else if (type == 'W' ) {
391 | c1 = MV; /* Temporary for Wings */
392 | if (!(dipent.flags & F_WINGS)) {
393 | errmsg("This game does not allow wings.\n");
394 | return E_WARN;
395 | }
396 | } else if (type == 'R') {
397 | c1 = MV;
398 | if (!(dipent.x2flags & X2F_ARTILLERY)) {
399 | errmsg("This game does not allow artillery.\n");
400 | return E_WARN;
401 | }
402 |
403 | } else {
404 | if (!c1)
405 | c1 = XC;
406 | for (t = (char *) pr[p1].move; *t; t++)
407 | if (*++t >> 4 == c1)
408 | break;
409 | if (!*t) {
410 | errmsg("Invalid coast specified for fleet in %s.\n",
411 | pr[p1].name);
412 | return E_WARN;
413 | }
414 | }
415 |
416 | break;
417 | case 'w':
418 | break;
419 |
420 | case 'm':
421 | break;
422 |
423 | case 'r':
424 | AddUnitProvinceToOrder(out_text,type, p1);
425 | break;
426 |
427 | case 'h': /* Home centre definition */
428 | case 'n': /* Home centre definition cancellation */
429 | if (!(dipent.x2flags & X2F_MORE_HOMES)) {
430 | errmsg("Invalid build order encountered.\n");
431 | return E_WARN;
432 | }
433 | if (pr[p1].type != 'x' &&
434 | !(pr[p1].type >= '0' && pr[p1].type <= '9') &&
435 | !(pr[p1].type >= 'A' && pr[p1].type <= 'Z')) {
436 | errmsg("Invalid province to make home centre.\n");
437 | return E_WARN;
438 | }
439 | AddProvinceToOrder(out_text, p1);
440 | break;
441 |
442 | case 't':
443 |
444 | AddUnitProvinceToOrder(out_text,type, p1);
445 | if (!(dipent.xflags & XF_TRANS_BUILD)) {
446 | errmsg("Build transformation is not enabled for this game.\n");
447 | return E_WARN;
448 | }
449 | if (water(p1)) {
450 | errmsg("Cannot transform while in/over water.\n");
451 | return E_WARN;
452 | }
453 | if (type == 'W' ) {
454 | if (!(dipent.flags & F_WINGS)) {
455 | errmsg("This game does not allow wings.\n");
456 | return E_WARN;
457 | }
458 | }
459 | if (type == 'R') {
460 | if (!(dipent.x2flags & X2F_ARTILLERY)) {
461 | errmsg("This game does not allow artillery.\n");
462 | return E_WARN;
463 | }
464 | }
465 |
466 |
467 | break;
468 |
469 | default:
470 | errmsg("Invalid build order encountered.\n");
471 | return E_WARN;
472 | }
473 | if (out_text != NULL) strcpy(out_string, out_text);
474 | return 0;
475 | }
476 | int buildin(char **s, int p)
477 | {
478 | /* Read build orders in from input file. */
479 |
480 | char type, order;
481 | unsigned char *t;
482 | int i, j, u=0, p1, c1, p2, pow;
483 |
484 | /*
485 | ** Process lines of the form:
486 | **
487 | ** cmd ::= <power>: {<order>} <type> <province>
488 | ** order ::= build | remove ¦ transform
489 | ** type ::= Army | Fleet | Wing
490 | */
491 |
492 |
493 | *s = get_order(*s, &order);
494 | *s = get_type(*s, &type);
495 | *s = get_prov(*s, &p1, &c1);
496 | if (!p1 && order != 'w' && order != 'u') {
497 | errmsg("Unrecognized province -> %s", *s);
498 | return E_WARN;
499 | }
500 | if (p == MASTER) {
501 | if (pr[p1].unit) {
502 | p = unit[pr[p1].unit].owner;
503 | } else {
504 | p = pr[p1].owner;
505 | }
506 | }
507 | if (IS_DUPLEX(dipent)) {
508 | /* See if I am ordering for one of my controlled powers */
509 | /* Either in its province or one of its units */
510 | p2 = pr[p1].owner;
511 | if (p2 == 0)
512 | if (pr[p1].unit)
513 | p2 = unit[pr[p1].unit].owner;
514 | if (p == PowerControlledBy(p2))
515 | p = p2; /* Yes, so become that power */
516 | }
517 | if (order == 'x' && !(dipent.xflags & XF_ANYDISBAND))
518 | order = nu[p] >= 0 ? 'b' : 'r';
519 |
520 | if (!(dipent.xflags & XF_ANYDISBAND))
521 | /* Do not check for waive condition if a duplex (non-intimate) game
522 | * This will be checked later in the waive code itself (Bug 366) */
523 | if ((order == 'b' && (nu[p] <= 0 && !(dipent.xflags & XF_ANYDISBAND) )) ||
524 | (!(IS_DUPLEX(dipent) && !(dipent.flags & F_INTIMATE)) && order == 'w' && (nu[p] <= 0 && !(dipent.xflags & XF_ANYDISBAND) )) ||
525 | (order == 'r' && nu[p] >= 0)) {
526 | errmsg("%s is not permitted to %s any units.\n",
527 | powers[p], order == 'r' ? "remove" : "build");
528 | return E_WARN;
529 | }
530 | switch (order) {
531 | case 'b':
532 | if (((dipent.xflags & XF_BUILD_ONECENTRE) == XF_BUILD_ONECENTRE) && one_owned[p] < 1) {
533 | errmsg("%s must own at least one home centre to build.\n",
534 | powers[p]);
535 | return E_WARN;
536 | }
537 | if (pr[p1].type == 'w' || pr[p1].type == 'l') {
538 | errmsg("%s is not a supply centre.\n",
539 | pr[p1].name);
540 | return E_WARN;
541 | }
542 | if (pr[p1].type != dipent.pl[p]
543 | && !(dipent.xflags & XF_BUILD_ANYCENTRES)) {
544 | errmsg("%s is not a home province for %s.\n",
545 | pr[p1].name, powers[p]);
546 | return E_WARN;
547 | }
548 | if (pr[p1].owner != p) {
549 | errmsg("%s does not control %s.\n",
550 | powers[p], pr[p1].name);
551 | return E_WARN;
552 | }
553 | if (type == 'x') {
554 | errmsg("Unit type must be specified for build.\n");
555 | return E_WARN;
556 | }
557 | if (pr[p1].blockaded) {
558 | errmsg("Cannot build in %s as it is blockaded.\n",
559 | pr[p1].name);
560 | return E_WARN;
561 | }
562 | if (type == 'A')
563 | c1 = MV;
564 | else if (type == 'W' ) {
565 | c1 = MV; /* Temporary for Wings */
566 | if (!(dipent.flags & F_WINGS)) {
567 | errmsg("This game does not allow wings.\n");
568 | return E_WARN;
569 | }
570 | } else if (type == 'R') {
571 | c1 = MV;
572 | if (!(dipent.x2flags & X2F_ARTILLERY)) {
573 | errmsg("This game does not allow artillery.\n");
574 | return E_WARN;
575 | }
576 | } else {
577 | if (!c1)
578 | c1 = XC;
579 | for (t = (char *) pr[p1].move; *t; t++)
580 | if (*++t >> 4 == c1)
581 | break;
582 | if (!*t) {
583 | errmsg("Invalid coast specified for fleet in %s.\n",
584 | pr[p1].name);
585 | return E_WARN;
586 | }
587 | }
588 |
589 | /* FALL THROUGH */
590 | case 'w':
591 |
592 | if (order == 'w' && (IS_DUPLEX(dipent) && !(dipent.flags & F_INTIMATE))) {
593 | /* If real duplex, need to specify power that want to waive for */
594 | get_power(*s, &pow);
595 |
596 | if (!pow) {
597 | pow = power(**s);
598 | if (pow <= 0 || pow >= WILD_PLAYER) {
599 | errmsg("Unrecognized power -> %s", *s);
600 | return E_WARN;
601 | }
602 | *s+= 2; /* Skip power letter and space */
603 | } else {
604 | *s = get_power(*s, &i); /* Consume the power text (if there) */
605 | }
606 | if (!(pow == p || PowerControlledBy(pow) == p)) {
607 | errmsg("Power is not controlled by you - invalid waive.\n");
608 | return E_WARN;
609 | } else
610 | p = pow; /* Become the power that you want to waive for */
611 | /* Bug 366, check if power does have builds to waive! */
612 | if (nu[p] <= 0 && !(dipent.xflags & XF_ANYDISBAND))
613 | {
614 | errmsg("%s is not permitted to build any units.\n",
615 | powers[p]);
616 | return E_WARN;
617 | }
618 | }
619 |
620 | if ((u = pr[p1].unit)) {
621 | if (unit[u].status != 'b' && unit[u].exists ) {
622 | errmsg("%s already has a unit present.\n", pr[p1].name);
623 | return E_WARN;
624 | } else {
625 | for (i = lu[p]; i != u && unit[i].order != u; i = unit[i].order);
626 | if (i == u)
627 | lu[p] = unit[i].order;
628 | else
629 | unit[i].order = unit[u].order;
630 | }
631 | pr[unit[u].loc].unit = 0;
632 |
633 | /*
634 | ** If he's building too many, the earliest built gets chucked.
635 | */
636 |
637 | } else if (nu[p] == 1) {
638 | for (u = lu[p], i = 0; unit[u].order; u = unit[u].order)
639 | i = u;
640 | if (i)
641 | unit[i].order = 0;
642 | else
643 | lu[p] = 0;
644 | pr[unit[u].loc].unit = 0;
645 | } else {
646 | nu[p]--;
647 | if (dipent.xflags & XF_ANYDISBAND) {
648 | if (nu[p] == 1) nu[p] = -2;
649 | }
650 | u = ++nunit;
651 | }
652 |
653 | if (dipent.xflags & XF_ANYDISBAND) {
654 | /* A Dirty fix that seems to do the trick */
655 | if (!u ) {
656 | nu[p]--;
657 | if (nu[p] == 1) nu[p] = -2;
658 | u = ++nunit;
659 | }
660 | }
661 |
662 |
663 | pr[p1].unit = order == 'w' ? 0 : u;
664 | unit[u].owner = p;
665 | unit[u].type = type;
666 | unit[u].stype = 'x';
667 | unit[u].loc = p1;
668 | unit[u].coast = c1;
669 | unit[u].status = order;
670 | unit[u].order = lu[p];
671 | lu[p] = u;
672 | break;
673 |
674 | case 'u': /* Unwaive, or remove waive orders */
675 | if (dipent.xflags & XF_ANYDISBAND)
676 | for (u = 1; u <= nunit; u++) {
677 | if (unit[u].owner == p && unit[u].type == 'x' ) {
678 | unit[u].owner = 0; /* not owned! */
679 | nu[p]++;
680 | if (nu[p] == -1)
681 | nu[p] = 2;
682 | }
683 | }
684 |
685 | break;
686 |
687 |
688 | case 'm':
689 | if (dipent.xflags & XF_ALTBUILD) {
690 | if (!(u = pr[p1].unit) || unit[u].owner != p || !unit[u].exists) {
691 | errmsg("%s does not own a unit %s %s to maintain.\n",
692 | powers[p], mov_type(p1,u), pr[p1].name);
693 | return E_WARN;
694 | }
695 | if (unit[u].status == 'd') {
696 | nu[p]--;
697 | if (nu[p] == 1)
698 | nu[p] = -2;
699 | }
700 |
701 | unit[u].order = 'm';
702 | unit[u].status = 'm';
703 | }
704 | break;
705 |
706 | case 'r':
707 | u = GetUnitIndex(p1, p); /* Will return the next unit to disband */
708 | if (!u || unit[u].owner != p) {
709 | errmsg("%s does not own a unit %s %s to remove.\n",
710 | powers[p], mov_type(p1,u), pr[p1].name);
711 | return E_WARN;
712 | }
713 | /*
714 | * If this one's already been removed, put it back.
715 | */
716 |
717 | if (!(dipent.xflags & XF_ANYDISBAND)) {
718 | if (unit[u].status == 'd') {
719 | nu[p]--;
720 | for (i = lu[p], j = 0; i && i != u; i = unit[i].order)
721 | j = i;
722 | if (j)
723 | unit[j].order = unit[u].order;
724 | else
725 | lu[p] = unit[u].order;
726 | }
727 | /*
728 | * If he's removing too many, the earliest one removed comes back.
729 | */
730 |
731 | if (nu[p] == -1) {
732 | for (i = lu[p], j = 0; unit[i].order; i = unit[i].order)
733 | j = i;
734 | if (j)
735 | unit[j].order = 0;
736 | else
737 | lu[p] = 0;
738 | unit[i].status = ':';
739 | } else {
740 | nu[p]++;
741 | }
742 | }
743 | else {
744 | if (dipent.xflags & XF_ANYDISBAND) {
745 | nu[p]++;
746 | if (nu[p] == -1)
747 | nu[p] = 2; /* Funny threshold, value always avoids +/-1 */
748 | }
749 | }
750 |
751 | unit[u].status = 'd';
752 | if (!(dipent.xflags & XF_ANYDISBAND)) {
753 | unit[u].order = lu[p];
754 | lu[p] = u;
755 | }
756 |
757 | break;
758 |
759 | case 't':
760 | u = pr[p1].unit; /* Find out what unit is there */
761 |
762 | if (!(dipent.xflags & XF_TRANS_BUILD)) {
763 | errmsg("Build transformation is not enabled for this game.\n");
764 | return E_WARN;
765 | }
766 |
767 | if (type == 'x') {
768 | errmsg("Unknown unit type to transform to.\n");
769 | return E_WARN;
770 | }
771 | if (unit[u].type == type && type != 'F') {
772 | errmsg("Error: Unit in %s is already of type %s.\n",
773 | pr[p1].name, Utype(unit[u].type));
774 | return E_WARN;
775 | }
776 | /* If a fleet, check that coast is different */
777 | if (type == 'F' && unit[u].type == 'F') {
778 | if (c1 == unit[u].coast) {
779 | errmsg("Fleet %s already on %s.\n", pr[p1].name, mtype[c1]);
780 | return E_WARN;
781 | }
782 | }
783 |
784 | /* OK, let's see if this can be transformed */
785 |
786 | switch (dipent.xflags & XF_TRANS_BANYW)
787 | {
788 | case 0: /* Can only transform on home centres */
789 | if (pr[p1].type != dipent.pl[p] && pr[p1].type != 'x' ) {
790 | errmsg("%s is not a home province for %s.\n",
791 | pr[p1].name, powers[p]);
792 | return E_WARN;
793 | }
794 | break;
795 |
796 | case XF_TRANS_BONEC: /* Need one home owned centre to transform */
797 | if (one_owned[p] < 1) {
798 | errmsg("%s must own at least one home centre to build.\n",
799 | powers[p]);
800 | return E_WARN;
801 | }
802 |
803 | case XF_TRANS_BANYC: /* Can transform on any centre */
804 | /* but must own the centre */
805 | if (pr[p1].owner != p) {
806 | errmsg("%s does not control %s.\n",
807 | powers[p], pr[p1].name);
808 | return E_WARN;
809 | }
810 | break;
811 |
812 | default: /*Anywhere at all */
813 | break; /* Nothing to do */
814 | }
815 | u = pr[p1].unit;
816 |
817 |
818 | if (!CheckOwnedOK(type, u, p, p1, &c1)) return E_WARN;
819 |
820 | /*
821 | * If this one's already been maked for removal, skip it.
822 | */
823 |
824 | if (unit[u].status == 'd' ) {
825 | errmsg("%s %s %s is already marked to be removed.\n",
826 | Utype(unit[u].type), mov_type(p1,u), pr[p1].name);
827 | return E_WARN;
828 | } else if (unit[u].status == 'b' ) {
829 | errmsg("%s %s %s is already marked to be built.\n",
830 | Utype(unit[u].type), mov_type(p1,u), pr[p1].name);
831 | return E_WARN;
832 | } else if (unit[u].type == type && unit[u].coast == c1) {
833 | /* Unit was marked to be transformed, cancel transform */
834 | unit[u].status = ' ';
835 | unit[u].order = ' ';
836 | } else {
837 | unit[u].status = 't';
838 | unit[u].order = order;
839 | unit[u].new_type = type;
840 | unit[u].new_coast = c1;
841 | /*lu[p] = u;*/
842 | }
843 | break;
844 |
845 | case 'h': /* Home centre definition */
846 | if (!(dipent.x2flags & X2F_MORE_HOMES)) {
847 | errmsg("Invalid build order encountered.\n");
848 | return E_WARN;
849 | }
850 | if (pr[p1].type != 'x' &&
851 | !(pr[p1].type >= '0' && pr[p1].type <= '9') &&
852 | !(pr[p1].type >= 'A' && pr[p1].type <= 'Z')) {
853 | errmsg("Invalid province to make home centre.\n");
854 | return E_WARN;
855 | }
856 | /* OK, see if it is owned */
857 | if (pr[p1].owner != p) {
858 | errmsg("Only owned centres can be made a home centre.\n");
859 | return E_WARN;
860 | }
861 | if (pr[p1].type == pletter[dipent.variant][p]) {
862 | errmsg("Centre is already owned by %s.\n",power(p));
863 | return E_WARN;
864 | }
865 | if (CountHomeCentres(p) >= dipent.num_homes) {
866 | errmsg("Power already has enough home centres.\n");
867 | return E_WARN;
868 | }
869 |
870 | if (pr[p1].type == 'x')
871 | pr[p1].home = p;
872 | else {
873 | if (CountCentres(power(pr[p1].type)) > 0) {
874 | errmsg("Centre is still used as home centre by %s.\n",
875 | powers[power(pr[p1].type)]);
876 | return E_WARN;
877 | } else {
878 | pr[p1].home = p;
879 | }
880 | }
881 | break;
882 |
883 |
884 | case 'n': /* Home centre cancel */
885 | if (!(dipent.x2flags & X2F_MORE_HOMES)) {
886 | errmsg("Invalid build order encountered.\n");
887 | return E_WARN;
888 | }
889 | pr[p1].home = pr[p1].type;
890 | break;
891 |
892 |
893 | default:
894 | errmsg("Invalid build order encountered.\n");
895 | return E_WARN;
896 | }
897 | return 0;
898 |
899 | }
900 |
901 | void buildout(int pt)
902 | {
903 | int i, u, p, c1, p_index, counting_centres, u_diff[NPOWER + 1], p1, pp;
904 | char mastrpt_pr[NPOWER + 1]; // Used to be [MAXPLAYERS]. DAN 04/02/1999
905 | int num_units[NPOWER +1];
906 | int num_hc;
907 | int pr_found;
908 | int one_printed;
909 | int assumed[ NPOWER + 1 ]; /* Count number of centres power is assuming this turn as home centre */
910 |
911 | fprintf(rfp, "Adjustment %s for Winter of %d. (%s.%s)\n\n",
912 | pt ? "orders" : "results",
913 | atoi(&dipent.phase[1]), dipent.name, dipent.seq);
914 |
915 | for ( u = 0; u <= NPOWER; u++)
916 | num_units[u] = 0; /* Initialise array */
917 |
918 | if (pt == MASTER) {
919 | for (u = 0; u <= NPOWER; u++) /* Used to be < MAXPLAYERS. DAN. */
920 | mastrpt_pr[u] = 0;
921 | for (u = 1; u <= nunit; u++) {
922 | if (unit[u].owner <= 0)
923 | continue;
924 | if (mastrpt_pr[unit[u].owner] != 1) {
925 | mastrpt_pr[unit[u].owner] = 1;
926 | mast_rpt(unit[u].owner, 1);
927 | };
928 | };
929 | fprintf(rfp, "\n");
930 | };
931 |
932 | for (p1 = 1; p1<= npr; p1++) {
933 | pr_found = 0;
934 | for (u = 1; u <= nunit; u++) {
935 | if (unit[u].loc == p1)
936 | pr_found = 1;
937 | }
938 | if (!pr_found && pr[p1].type == 'x') {
939 | /* Unowned centre: if we have natives, build one there */
940 | if (dipent.has_natives &&
941 | (pr[p1].owner == dipent.has_natives || !pr[p1].owner)) {
942 | unit[++nunit].type = 'A';
943 | unit[nunit].stype = 'x';
944 | unit[nunit].owner = dipent.has_natives;
945 | unit[nunit].status = 'b';
946 | unit[nunit].loc = p1;
947 | }
948 | }
949 | }
950 |
951 | for (p = 1; p <= NPOWER; p++) { /* This loop is just to order by power */
952 | p_index = FindPower(p);
953 | one_printed = 0;
954 | if (p_index >= dipent.n) continue; /* Not a valid power */
955 | if (!(processing || pt == p || pt == MASTER || pt == PowerControlledBy(p))) continue;
956 | for (u = 1; u <= nunit; u++) {
957 | if (p == unit[u].owner) {
958 |
959 | if (unit[u].status == 'b') {
960 |
961 | num_units[unit[u].owner]++;
962 | fprintf(rfp, "%s: ", powers[unit[u].owner]);
963 | for (i = strlen(powers[p]); i < LPOWER; i++)
964 | putc(' ', rfp);
965 | fprintf(rfp, "Builds %s in %s", autype(unit[u].type),
966 | pr[unit[u].loc].name);
967 | if ((c1 = unit[u].coast) > XC)
968 | fprintf(rfp, " (%s).\n", mtype[c1]);
969 | else
970 | fprintf(rfp, ".\n");
971 | unit[u].status = ':';
972 | one_printed++;
973 |
974 | } else if (unit[u].status == 'd' && unit[u].exists) {
975 | fprintf(rfp, "%s: ", powers[unit[u].owner]);
976 | for (i = strlen(powers[p]); i < LPOWER; i++)
977 | putc(' ', rfp);
978 | fprintf(rfp, "Removes the %s %s %s.\n",
979 | utype(unit[u].type),
980 | mov_type(unit[u].loc,u),
981 | pr[unit[u].loc].name);
982 |
983 | unit[u].owner = 0;
984 | unit[u].status = ':';
985 | pr[unit[u].loc].unit_held = 0;
986 | one_printed++;
987 |
988 |
989 | } else if (unit[u].status == 'w') {
990 | num_units[unit[u].owner]++;
991 | fprintf(rfp, "%s: ", powers[unit[u].owner]);
992 | for (i = strlen(powers[p]); i < LPOWER; i++)
993 | putc(' ', rfp);
994 | fprintf(rfp, "Build waived.\n");
995 | unit[u].owner = 0;
996 | one_printed++;
997 |
998 | } else if (unit[u].status == 't') {
999 | num_units[unit[u].owner]++;
1000 | /* Only notify when changing type */
1001 | fprintf(rfp, "%s: ", powers[unit[u].owner]);
1002 | for (i = strlen(powers[p]); i < LPOWER; i++)
1003 | putc(' ', rfp);
1004 | fprintf(rfp, "Transform the %s %s %s to %s",
1005 | utype(unit[u].type),
1006 | mov_type(unit[u].loc,u),
1007 | pr[unit[u].loc].name,
1008 | autype(unit[u].new_type));
1009 | if ((c1 = unit[u].new_coast) > XC)
1010 | fprintf(rfp, " (%s).\n", mtype[c1]);
1011 | else
1012 | fprintf(rfp, ".\n");
1013 | if (processing) {
1014 | unit[u].type = unit[u].new_type;
1015 | unit[u].coast = unit[u].new_coast;
1016 | }
1017 | unit[u].status = ':';
1018 | one_printed++;
1019 | } else if (unit[u].status == 'm') {
1020 | num_units[unit[u].owner]++;
1021 | /* only notify when explictly maintaining units */
1022 | if (!processing && !predict) {
1023 | fprintf(rfp, "%s: ", powers[unit[u].owner]);
1024 | for (i = strlen(powers[p]); i < LPOWER; i++)
1025 | putc(' ', rfp);
1026 | fprintf(rfp, "Maintains the %s %s %s.\n",
1027 | utype(unit[u].type),
1028 | mov_type(unit[u].loc,u),
1029 | pr[unit[u].loc].name);
1030 | one_printed++;
1031 | }
1032 | unit[u].status = ':';
1033 | } else if (unit[u].status == ':') {
1034 | num_units[unit[u].owner]++;
1035 | if (dipent.xflags & XF_ALTBUILD) {
1036 | fprintf(rfp, "%s: ", powers[unit[u].owner]);
1037 | for (i = strlen(powers[p]); i < LPOWER; i++)
1038 | putc(' ', rfp);
1039 | fprintf(rfp, "No order for the %s %s %s",
1040 | utype(unit[u].type),
1041 | mov_type(unit[u].loc,u),
1042 | pr[unit[u].loc].name);
1043 | if (!processing && !predict ) {
1044 | more_orders++;
1045 | }
1046 | fprintf(rfp, " (maintain).\n");
1047 | one_printed++;
1048 | }
1049 | }
1050 | }
1051 | }
1052 | if (!processing && !predict && pt == MASTER )
1053 | if (one_printed)
1054 | fprintf(rfp,"\n"); /* Extra blank for master */
1055 |
1056 | }
1057 |
1058 | for (p = 1; p <= NPOWER; p++) {
1059 | assumed[p]=0;
1060 | if (processing || pt == p || pt == MASTER || PowerControlledBy(p) == pt) {
1061 | p_index = FindPower(p);
1062 | if (p_index >= dipent.n) continue; /* Not a valid power */
1063 | counting_centres = dipent.players[p_index].centers -
1064 | dipent.players[p_index].centres_blockaded;
1065 | u_diff[p] = num_units[p] - counting_centres - ExtraCentres(p);
1066 |
1067 | if (u_diff[p] < 0) {
1068 | fprintf(rfp, "%s: ", powers[p]);
1069 | for (i = strlen(powers[p]); i < LPOWER; i++)
1070 | putc(' ', rfp);
1071 | for (i = 1; i <= npr; i++) {
1072 | if (pr[i].owner == p && !pr[i].unit &&
1073 | (pr[i].type == dipent.pl[p] || (centre(i) &&
1074 | (dipent.xflags & XF_BUILD_ANYCENTRES))))
1075 | break;
1076 | }
1077 | if (!(dipent.xflags & XF_ANYDISBAND)) {
1078 | if (i > npr ) {
1079 | i = nu[p] - 1 + ExtraCentres(p);
1080 | if (processing)
1081 | fprintf(rfp, "%d unusable build%s waived.\n", i, i == 1 ? "" : "s");
1082 | else
1083 | fprintf(rfp, "%d unusable build%s pending.\n", i, i == 1 ? "" : "s");
1084 | nu[p] = 1;
1085 | } else {
1086 | i = nu[p] - 1;
1087 |
1088 | fprintf(rfp, "%d build%s pending.\n", i, i == 1 ? "" : "s");
1089 | }
1090 | }
1091 | }
1092 | if ((dipent.xflags & XF_ANYDISBAND) && u_diff[p] != 0 ) {
1093 | if (!processing && !predict && !(pt == MASTER))
1094 | err++;
1095 | if (u_diff[p] > 0)
1096 | fprintf(rfp,"%d too many units: need to remove some.\n", u_diff[p]);
1097 | else
1098 | fprintf(rfp,"%d too few units: need to build/maintain/waive.\n", -u_diff[p]);
1099 | }
1100 | }
1101 | }
1102 |
1103 | if (dipent.xflags & XF_ALTBUILD) {
1104 | if (err)
1105 | more_orders++; /* MUST be 100% ok for these variants */
1106 |
1107 | } else {
1108 | if (!processing && (nu[pt] < -1 || nu[pt] > 1))
1109 | more_orders++;
1110 | }
1111 |
1112 | for (u = 1; u <= nunit; u++) {
1113 | if ((p = unit[u].owner) &&
1114 | u_diff[p] > 0 &&
1115 | pr[unit[u].loc].owner != p &&
1116 | FindPower(p) < dipent.n) {
1117 | nu[p]++;
1118 | unit[u].owner = 0;
1119 | if (processing || pt == p || pt == MASTER || PowerControlledBy(p) == pt) {
1120 | fprintf(rfp, "%s: ", powers[p]);
1121 | for (i = strlen(powers[p]); i < LPOWER; i++)
1122 | putc(' ', rfp);
1123 | fprintf(rfp, "Defaults, removing the %s in %s%s.\n",
1124 | utype(unit[u].type),
1125 | water(unit[u].loc) ? "the " : "",
1126 | pr[unit[u].loc].name);
1127 | u_diff[p]--;
1128 | }
1129 | }
1130 | }
1131 |
1132 | /* Now see if there are centres that have become home centres */
1133 | for (p1 = 1; p1<= npr && (dipent.x2flags & X2F_HOMETRANSFER); p1++) {
1134 | if (pr[p1].unit && pr[p1].unit_held &&
1135 | (PossibleHomeCentre(pletter[dipent.variant][pr[p1].home]) ||
1136 | (!pr[p1].home && pr[p1].type == 'x')) &&
1137 | unit[pr[p1].unit].owner != pr[p1].home) {
1138 | pr[p1].home = unit[pr[p1].unit].owner;
1139 | }
1140 | }
1141 |
1142 | for (pp = 1; pp <= NPOWER; pp++) { /* This loop is just to order by power */
1143 | p_index = FindPower(pp);
1144 | one_printed = 0;
1145 | if (p_index >= dipent.n) continue; /* Not a valid power */
1146 | one_printed = 0;
1147 | for (p1 = 1; p1<= npr; p1++) {
1148 | p = pr[p1].home;
1149 | if ( pr[p1].type != pletter[dipent.variant][pr[p1].home] &&
1150 | PossibleHomeCentre(pletter[dipent.variant][pr[p1].home])) {
1151 | assumed[p]++;
1152 | if (pp == p && (processing || pt == p || pt == MASTER || PowerControlledBy(p) == pt)) {
1153 | one_printed++;
1154 | fprintf(rfp, "\n%s: ", powers[p]);
1155 | for (i = strlen(powers[p]); i < LPOWER; i++)
1156 | putc(' ', rfp);
1157 | fprintf(rfp, "Assumes %s as a home centre.",
1158 | pr[p1].name);
1159 | }
1160 | }
1161 | }
1162 | }
1163 | if (one_printed) fprintf(rfp, "\n");
1164 |
1165 | /* Now show pending moves for home centre declarations */
1166 | one_printed = 0;
1167 | for (p = 1; p <= NPOWER && (dipent.x2flags & X2F_MORE_HOMES); p++) {
1168 | if (p != dipent.has_natives && (processing || pt == p || pt == MASTER || PowerControlledBy(p) == pt )) {
1169 | p_index = FindPower(p);
1170 | if (dipent.players[p_index].centers <=0 &&
1171 | dipent.players[p_index].units <= 0)
1172 | continue; /* Not an alive player, so ignore */
1173 | num_hc = CountHomeCentres(p);
1174 | if ( (dipent.x2flags & X2F_MORE_HOMES) &&
1175 | num_hc + assumed[p] < dipent.num_homes && CountCentres(p) > 0) {
1176 | fprintf(rfp, "\n%s: ", powers[p]);
1177 | one_printed++;
1178 | for (i = strlen(powers[p]); i < LPOWER; i++)
1179 | putc(' ', rfp);
1180 | fprintf(rfp, "%d more Home Centre assignment%s pending.",
1181 | dipent.num_homes - num_hc - assumed[p],
1182 | dipent.num_homes - num_hc - assumed[p] > 1 ? "s" : "" );
1183 | }
1184 | }
1185 | }
1186 |
1187 | if (one_printed) fprintf(rfp, "\n");
1188 |
1189 | /* Adjust the unit_held values */
1190 | for (p1 = 1; p1<= npr && processing; p1++) {
1191 | if (pr[p1].unit )
1192 | pr[p1].unit_held = 1;
1193 | }
1194 |
1195 | if (processing && dipent.flags & F_INTIMATE)
1196 | PrintTreasury(pt, NULL, processing, predict);
1197 |
1198 | }
1199 | /****************************************************************************/