]> git.pond.sub.org Git - empserver/blob - src/lib/subs/attsub.c
Break inclusion cycle: prototypes.h and commands.h included each
[empserver] / src / lib / subs / attsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2006, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  attsub.c: Attack subroutines
29  * 
30  *  Known contributors to this file:
31  *     Ken Stevens, 1995
32  *     Steve McClure, 1996-2000
33  *     Markus Armbruster, 2006
34  */
35
36 #include <config.h>
37
38 #include <ctype.h>
39 #include <math.h>
40 #include "combat.h"
41 #include "file.h"
42 #include "item.h"
43 #include "land.h"
44 #include "lost.h"
45 #include "map.h"
46 #include "misc.h"
47 #include "mission.h"
48 #include "nat.h"
49 #include "news.h"
50 #include "nsc.h"
51 #include "optlist.h"
52 #include "path.h"
53 #include "plague.h"
54 #include "player.h"
55 #include "prototypes.h"
56 #include "sect.h"
57 #include "ship.h"
58 #include "treaty.h"
59 #include "xy.h"
60
61 #define CASUALTY_LUMP   1       /* How big casualty chunks should be */
62
63 static void ask_olist(int combat_mode, struct combat *off,
64                       struct combat *def, struct emp_qelem *olist,
65                       char *land_answer, int *a_spyp, int *a_engineerp);
66 static void take_move_in_mob(int combat_mode, struct llist *llp,
67                              struct combat *off, struct combat *def);
68 static void move_in_land(int combat_mode, struct combat *off,
69                          struct emp_qelem *olist, struct combat *def);
70 static void ask_move_in(struct combat *off, struct emp_qelem *olist,
71                         struct combat *def);
72 static void ask_move_in_off(struct combat *off, struct combat *def);
73
74 static int board_abort(struct combat *off, struct combat *def);
75 static int land_board_abort(struct combat *off, struct combat *def);
76 static int ask_off(int combat_mode, struct combat *off,
77                    struct combat *def);
78 static void get_dlist(struct combat *def, struct emp_qelem *list, int a_spy,
79                       int *d_spyp);
80 static int get_ototal(int combat_mode, struct combat *off,
81                       struct emp_qelem *olist, double osupport, int check);
82 static int get_dtotal(struct combat *def, struct emp_qelem *list,
83                       double dsupport, int check);
84 static double att_calcodds(int, int);
85 static int take_casualty(int combat_mode, struct combat *off,
86                          struct emp_qelem *olist);
87
88 static void send_reacting_units_home(struct emp_qelem *list);
89 static int take_def(int combat_mode, struct emp_qelem *list,
90                     struct combat *off, struct combat *def);
91
92 static int get_land(int combat_mode, struct combat *def, int uid,
93                     struct llist *llp, int victim_land);
94
95 char *att_mode[] = {
96     /* must match combat types in combat.h */
97     "defend", "attack", "assault", "paradrop", "board", "lboard"
98 };
99
100
101 /*
102  * The principal object in this code is the "combat" object.  A combat object
103  * is either a sector or ship.  There are
104  * usually two instances of this, the "def" or defense combat object, and
105  * the array of "off" or offense objects.  The number of offense objects is
106  * determined by the value of off->last (e.g. more than one attacking sector).
107  * the type of the object is determined by combat->type which can take the
108  * values EF_SECTOR, EF_SHIP, EF_PLANE, or EF_BAD.  Another important parameter
109  * which is often passed to these functions is combat_mode.  This can take
110  * the value A_DEFEND, A_ATTACK, A_ASSAULT, A_PARA, A_BOARD and A_LBOARD.
111  * As these six modes of being in combat affect things like mobcost and combat
112  * value, there are often switches made on combat_mode.  Note that in all cases
113  * no mobility is taken from sectors, ships, or land units until the player
114  * has committed to a fight.  Instead, the cost is temporarily placed in
115  * combat->mobcost, or llp->mobil as the case may be, and then when the object
116  * is "put" back onto disk, then the amounts in these variables are subtracted
117  * from the object's mobility.  It needs to be done this way as the objects
118  * are constantly being re-read from disk, and we don't want to take any mob
119  * unless a fight actually occurrs.
120  * -Ken Stevens
121  */
122
123 /* initialize combat object */
124
125 int
126 att_combat_init(struct combat *com, int type)
127 {
128     memset(com, 0, sizeof(*com));
129     com->type = type;
130     return type;
131 }
132
133 /* print a combat object with optional preposition */
134
135 static char *
136 pr_com(int inon, struct combat *com, natid who)
137 {
138     if (com->type == EF_SECTOR) {
139         return prbuf("%s%s",
140                      inon ? inon == 1 ? "in " : "into " : "",
141                      xyas(com->x, com->y, who));
142     } else if (com->type == EF_SHIP) {
143         return prbuf("%s%s %s(#%d)",
144                      inon ? inon == 1 ? "on " : "onto " : "",
145                      com->shp_mcp->m_name, com->shp_name,
146                      com->shp_uid);
147     } else if (com->type == EF_LAND) {
148         return prbuf("%s%s #%d",
149                      inon ? inon == 1 ? "on " : "onto " : "",
150                      com->lnd_lcp->l_name, com->lnd_uid);
151     } else {
152         return "your forces";
153     }
154 }
155
156 static char *
157 prcom(int inon, struct combat *com)
158 {
159     return pr_com(inon, com, player->cnum);
160 }
161
162 /*
163  * This is the combat object "type" based integrity check.  It basically
164  * splits along three divisions: ship/sector, attacker/defender, 
165  * first time/not first time.
166  */
167
168 int
169 att_get_combat(struct combat *com, int isdef)
170 {
171     struct sctstr sect;
172     struct shpstr ship;
173     struct lndstr land;
174     int pstage;
175     natid owner;
176     int mil;
177     int eff;
178     int mob;
179     coord x, y;
180
181     switch (com->type) {
182     case EF_SECTOR:
183         if (!getsect(com->x, com->y, &sect)) {
184             pr("Bad sector: %s\n", xyas(com->x, com->y, player->cnum));
185             return att_combat_init(com, EF_BAD);
186         }
187         com->sct_type = sect.sct_type;
188         com->sct_dcp = &dchr[sect.sct_type];
189         mil = sect.sct_item[I_MILIT];
190         pstage = sect.sct_pstage;
191         owner = sect.sct_own;
192         eff = sect.sct_effic;
193         mob = sect.sct_mobil;
194         x = com->x;
195         y = com->y;
196         break;
197     case EF_LAND:
198         if (!getland(com->lnd_uid, &land) || !land.lnd_own) {
199             if (isdef)
200                 pr("Land unit #%d is not in the same sector!\n",
201                    com->lnd_uid);
202             return att_combat_init(com, EF_BAD);
203         }
204         if (isdef && player->owner) {
205             pr("Boarding yourself?  Try using the 'load' command.\n");
206             return att_combat_init(com, EF_BAD);
207         }
208         com->lnd_lcp = &lchr[(int)land.lnd_type];
209         mil = land.lnd_item[I_MILIT];
210         pstage = land.lnd_pstage;
211         owner = land.lnd_own;
212         eff = land.lnd_effic;
213         mob = land.lnd_mobil;
214         x = land.lnd_x;
215         y = land.lnd_y;
216         break;
217     case EF_SHIP:
218         if (!getship(com->shp_uid, &ship) || !ship.shp_own) {
219             if (isdef)
220                 pr("Ship #%d is not in the same sector!\n", com->shp_uid);
221             else
222                 pr("Ship #%d is not your ship!\n", com->shp_uid);
223             return att_combat_init(com, EF_BAD);
224         }
225         if (opt_MARKET) {
226             if (isdef && player->owner &&
227                 ontradingblock(EF_SHIP, &ship)) {
228                 pr("%s is on the trading block.\n", prcom(0, com));
229                 return att_combat_init(com, EF_BAD);
230             }
231         }
232         if (isdef && player->owner) {
233             pr("Boarding yourself?  Try using the 'tend' command.\n");
234             return att_combat_init(com, EF_BAD);
235         }
236         com->shp_mcp = &mchr[(int)ship.shp_type];
237         strncpy(com->shp_name, ship.shp_name, MAXSHPNAMLEN);
238         if (!isdef && !player->owner) {
239             if (com->set)
240                 pr("%s was just sunk!\n", prcom(0, com));
241             else
242                 pr("Ship #%d is not your ship!\n", com->shp_uid);
243             return att_combat_init(com, EF_BAD);
244         }
245         mil = ship.shp_item[I_MILIT];
246         pstage = ship.shp_pstage;
247         owner = ship.shp_own;
248         eff = ship.shp_effic;
249         mob = ship.shp_mobil;
250         x = ship.shp_x;
251         y = ship.shp_y;
252         break;
253     case EF_PLANE:
254         return com->mil;
255     case EF_BAD:
256         return EF_BAD;
257     default:
258         return att_combat_init(com, EF_BAD);
259     }
260
261     if (!com->set) {            /* first time */
262         if (isdef) {            /* defender */
263             com->troops = mil;
264         } else {                /* attacker */
265             if (!mil)
266                 pr("No mil %s\n", prcom(1, com));
267             else if (mil == 1)
268                 pr("Only 1 mil %s\n", prcom(1, com));
269             /* don't abandon attacking sectors or ships */
270             com->troops = MAX(0, mil - 1);
271         }
272         com->plague = pstage == PLG_INFECT;
273     } else {                    /* not first time */
274         if (isdef) {            /* defender */
275             if (com->x != x || com->y != y) {
276                 pr("%s has moved!\n", prcom(0, com));
277                 return att_combat_init(com, EF_BAD);
278             }
279             if (owner != com->own) {
280                 if (owner) {
281                     pr("WARNING: The ownership of %s just changed from %s to %s!\n",
282                        prcom(0, com), cname(com->own), cname(owner));
283                 } else if (com->type == EF_SECTOR) {
284                     pr("WARNING: %s just abandoned sector %s!\n",
285                        cname(com->own),
286                        xyas(com->x, com->y, player->cnum));
287                 }
288             }
289             if (com->mil != mil)
290                 pr("WARNING: The enemy mil %s just %s from %d to %d!\n",
291                    prcom(1, com),
292                    com->mil < mil ? "increased" : "decreased", com->mil,
293                    mil);
294             com->troops = mil;
295         } else {                /* attacker */
296             if (owner != player->cnum && getrel(getnatp(owner), player->cnum) != ALLIED) {
297                 /* must be EF_SECTOR */
298                 if (com->mil)
299                     pr("WARNING: Your %d mil in %s were destroyed because %s just took the sector!\n",
300                        com->mil, xyas(com->x, com->y, player->cnum),
301                        cname(owner));
302                 else
303                     pr("You no longer own %s\n",
304                        xyas(com->x, com->y, player->cnum));
305                 return att_combat_init(com, EF_BAD);
306             }
307             if (com->troops && com->troops + 1 > mil) {
308                 if (com->own == owner && player->cnum == owner)
309                     /* not a takeover */
310                     pr("WARNING: Your mil %s has been reduced from %d to %d!\n",
311                        prcom(1, com), com->troops, MAX(0, mil - 1));
312                 com->troops = MAX(0, mil - 1);
313             }
314         }
315     }
316     com->set = 1;
317     com->mil = mil;
318     com->own = owner;
319     com->x = x;
320     com->y = y;
321     com->eff = eff;
322     com->mob = mob;
323     return com->troops;
324 }
325
326 /*
327  * In the course of the fight, the combat object may have lost mil, eff, or
328  * mobility.  This is the place where the data in the object gets flushed to
329  * disk to make it "real".
330  */
331
332 static void
333 put_combat(struct combat *com)
334 {
335     struct sctstr sect;
336     struct shpstr ship;
337     struct lndstr land;
338     int deff;
339
340     switch (com->type) {
341     case EF_SECTOR:
342         getsect(com->x, com->y, &sect);
343         sect.sct_type = com->sct_type;
344         deff = sect.sct_effic - com->eff;
345         if (deff > 0) {
346             sect.sct_road -= sect.sct_road * deff / 100.0;
347             sect.sct_rail -= sect.sct_rail * deff / 100.0;
348             sect.sct_defense -= sect.sct_defense * deff / 100.0;
349             if (sect.sct_road <= 0)
350                 sect.sct_road = 0;
351             if (sect.sct_rail <= 0)
352                 sect.sct_rail = 0;
353             if (sect.sct_defense <= 0)
354                 sect.sct_defense = 0;
355         }
356         sect.sct_effic = com->eff;
357         if (com->mobcost) {
358             if (opt_MOB_ACCESS) {
359                 if ((com->mob - com->mobcost) < -127)
360                     sect.sct_mobil = -127;
361                 else
362                     sect.sct_mobil = (short)(com->mob - com->mobcost);
363             } else {
364                 if ((com->mob - com->mobcost) < 0)
365                     sect.sct_mobil = 0;
366                 else
367                     sect.sct_mobil = (short)(com->mob - com->mobcost);
368             }
369         }
370         makelost(EF_SECTOR, sect.sct_own, 0, sect.sct_x, sect.sct_y);
371         makenotlost(EF_SECTOR, com->own, 0, sect.sct_x, sect.sct_y);
372         sect.sct_own = com->own;
373         if (com->plague) {
374             if (sect.sct_pstage == PLG_HEALTHY)
375                 sect.sct_pstage = PLG_EXPOSED;
376         }
377         sect.sct_item[I_MILIT] = com->mil;
378         putsect(&sect);
379         com->own = sect.sct_own;        /* avoid WARNING if sector reverts */
380         break;
381     case EF_LAND:
382         getland(com->lnd_uid, &land);
383         land.lnd_effic = com->eff;
384         if (com->mobcost) {
385             if (com->mob - com->mobcost < -127)
386                 land.lnd_mobil = -127;
387             else
388                 land.lnd_mobil = (signed char)(com->mob - com->mobcost);
389         }
390         makelost(EF_LAND, land.lnd_own, land.lnd_uid,
391                  land.lnd_x, land.lnd_y);
392         land.lnd_own = com->own;
393         makenotlost(EF_LAND, land.lnd_own, land.lnd_uid,
394                     land.lnd_x, land.lnd_y);
395         if (com->plague) {
396             if (land.lnd_pstage == PLG_HEALTHY)
397                 land.lnd_pstage = PLG_EXPOSED;
398         }
399         if (!(com->lnd_lcp->l_flags & L_SPY))
400             land.lnd_item[I_MILIT] = com->mil;
401         lnd_count_units(&land);
402         if (com->own == player->cnum) {
403             land.lnd_mission = 0;
404             land.lnd_rflags = 0;
405             memset(land.lnd_rpath, 0, sizeof(land.lnd_rpath));
406         }
407         putland(com->lnd_uid, &land);
408         break;
409     case EF_SHIP:
410         getship(com->shp_uid, &ship);
411         ship.shp_effic = com->eff;
412         if (com->mobcost) {
413             if (com->mob - com->mobcost < -127)
414                 ship.shp_mobil = -127;
415             else
416                 ship.shp_mobil = (signed char)(com->mob - com->mobcost);
417         }
418         makelost(EF_SHIP, ship.shp_own, ship.shp_uid,
419                  ship.shp_x, ship.shp_y);
420         ship.shp_own = com->own;
421         makenotlost(EF_SHIP, ship.shp_own, ship.shp_uid,
422                     ship.shp_x, ship.shp_y);
423         if (com->plague) {
424             if (ship.shp_pstage == PLG_HEALTHY)
425                 ship.shp_pstage = PLG_EXPOSED;
426         }
427         ship.shp_item[I_MILIT] = com->mil;
428         count_units(&ship);
429         if (com->own == player->cnum) {
430             ship.shp_mission = 0;
431             ship.shp_rflags = 0;
432             memset(ship.shp_rpath, 0, sizeof(ship.shp_rpath));
433         }
434         putship(com->shp_uid, &ship);
435     }
436     com->mobcost = 0;
437     att_get_combat(com, com->own != player->cnum);
438 }
439
440 /* If pre-attack, abort fight.  If post-attack, don't move anything in */
441
442 static int
443 abort_attack(void)
444 {
445     return player->aborted = 1;
446 }
447
448 /*
449  * This is the combat_mode based integrity check.  It splits among two main
450  * divisions: first time/not first time, and attack/assault/para/board.
451  */
452
453 int
454 att_abort(int combat_mode, struct combat *off, struct combat *def)
455 {
456     struct sctstr sect;
457     int rel;
458     char y_or_n[512];
459     struct natstr *natp;
460
461     if (player->aborted)
462         return 1;
463     if (att_get_combat(def, 1) < 0)
464         return abort_attack();
465
466     if (off && combat_mode != A_ATTACK) {
467         if (att_get_combat(off, 0) < 0)
468             return abort_attack();
469         if (off->type == EF_SHIP &&
470             (!getsect(off->x, off->y, &sect) ||
471              sect.sct_type != SCT_WATER)) {
472             pr("%s can not %s from that far inland!\n",
473                prcom(0, off), att_mode[combat_mode]);
474             return abort_attack();
475         }
476     }
477     switch (combat_mode) {
478     case A_ATTACK:
479         if (!neigh(def->x, def->y, player->cnum) &&
480             !adj_units(def->x, def->y, player->cnum)) {
481             pr("You are not adjacent to %s\n",
482                xyas(def->x, def->y, player->cnum));
483             return abort_attack();
484         }
485         if (def->own == player->cnum) {
486             pr("You can't attack your own sector.\n");
487             return abort_attack();
488         }
489         break;
490     case A_ASSAULT:
491         if (off && mapdist(off->x, off->y, def->x, def->y) > 1) {
492             pr("You'll have to get there first...\n");
493             return abort_attack();
494         }
495         if (off && def->sct_type == SCT_MOUNT) {
496             pr("You can't assault a %s sector!\n", def->sct_dcp->d_name);
497             return abort_attack();
498         }
499         break;
500     case A_PARA:
501         if (def->own == player->cnum) {
502             pr("You can't air-assault your own sector.\n");
503             return abort_attack();
504         }
505         if (off && (def->sct_type == SCT_MOUNT ||
506                     def->sct_type == SCT_WATER ||
507                     def->sct_type == SCT_CAPIT ||
508                     def->sct_type == SCT_FORTR ||
509                     def->sct_type == SCT_WASTE)) {
510             pr("You can't air-assault a %s sector!\n",
511                def->sct_dcp->d_name);
512             return abort_attack();
513         }
514         break;
515     case A_BOARD:
516         return board_abort(off, def);
517     case A_LBOARD:
518         return land_board_abort(off, def);
519     }
520
521     if (off && def->sct_dcp->d_mob0 < 0) {
522         pr("You can't %s a %s sector!\n",
523            att_mode[combat_mode], def->sct_dcp->d_name);
524         return abort_attack();
525     }
526     if (!off || off->relations_checked)
527         return 0;
528     off->relations_checked = 1;
529
530     if (opt_HIDDEN) {
531         setcont(player->cnum, def->own, FOUND_SPY);
532         setcont(def->own, player->cnum, FOUND_SPY);
533     }
534     if (opt_SLOW_WAR && def->own != player->cnum) {
535         natp = getnatp(player->cnum);
536         rel = getrel(natp, def->own);
537
538         if (rel == ALLIED) {
539             sprintf(y_or_n, "Sector is owned by %s, your ally, %s [yn]? ",
540                     cname(def->own), att_mode[combat_mode]);
541             if (!confirm(y_or_n))
542                 return abort_attack();
543
544         }
545         if ((rel != AT_WAR) && (def->own) &&
546             (sect.sct_oldown != player->cnum)) {
547             pr("You're not at war with them!\n");
548             return abort_attack();
549         }
550     }
551     return 0;
552 }
553
554 /*
555  * Lots of special things need to be checked for boarding, so I put it in
556  * it's own function.
557  */
558
559 static int
560 board_abort(struct combat *off, struct combat *def)
561 {
562     struct shpstr aship, dship; /* for tech levels */
563     struct sctstr sect;
564
565     if (att_get_combat(def, 1) < 0)
566         return abort_attack();
567
568     if (!off)
569         return 0;
570
571     if (att_get_combat(off, 0) < 0)
572         return abort_attack();
573
574     if (off->x != def->x || off->y != def->y) {
575         pr("Ship #%d is not in the same sector!\n", def->shp_uid);
576         return abort_attack();
577     }
578     if (off->type == EF_SHIP) {
579         if (off->mob <= 0) {
580             pr("%s has no mobility!\n", prcom(0, off));
581             return abort_attack();
582         }
583         getship(off->shp_uid, &aship);
584         getship(def->shp_uid, &dship);
585         if (techfact(aship.shp_tech, 1.0) * aship.shp_speed * off->eff
586             <= techfact(dship.shp_tech, 1.0) * dship.shp_speed * def->eff) {
587             pr("Victim ship moves faster than you do!\n");
588             if (def->own)
589                 wu(0, def->own,
590                    "%s (#%d) %s failed to catch %s\n",
591                    cname(aship.shp_own), aship.shp_own,
592                    pr_com(0, off, def->own), pr_com(0, def, def->own));
593             return abort_attack();
594         }
595     } else if (off->type != EF_SECTOR) {
596         pr("Please tell the deity that you got the 'banana boat' error\n");
597         return abort_attack();
598     }
599     if (def->shp_mcp->m_flags & M_SUB) {
600         getsect(def->x, def->y, &sect);
601         if (sect.sct_type == SCT_WATER) {
602             pr("You can't board a submarine!\n");
603             return abort_attack();
604         }
605     }
606     return 0;
607 }
608
609 /*
610  * Lots of special things need to be checked for boarding, so I put it in
611  * it's own function.
612  * STM - I copied it for land unit boarding. :)
613  */
614
615 static int
616 land_board_abort(struct combat *off, struct combat *def)
617 {
618     if (att_get_combat(def, 1) < 0)
619         return abort_attack();
620
621     if (!off)
622         return 0;
623
624     if (att_get_combat(off, 0) < 0)
625         return abort_attack();
626
627     if (off->x != def->x || off->y != def->y) {
628         pr("Land unit #%d is not in the same sector!\n", def->lnd_uid);
629         return abort_attack();
630     }
631
632     return 0;
633 }
634
635 /* If we are boarding, then the defending ship gets a chance to fire back */
636 int
637 att_approach(struct combat *off, struct combat *def)
638 {
639     int dam;
640     struct sctstr sect;
641     struct shpstr ship;
642
643     pr("Approaching %s...\n", prcom(0, def));
644     if (def->own)
645         wu(0, def->own,
646            "%s is being approached by %s...\n",
647            pr_com(0, def, def->own), pr_com(0, off, def->own));
648     if (!(dam = shipdef(player->cnum, def->own, def->x, def->y)))
649         return 0;
650
651     pr("They're firing at us sir!\n");
652     if (def->own) {
653         wu(0, def->own,
654            "Your fleet at %s does %d damage to %s\n",
655            xyas(def->x, def->y, def->own), dam, pr_com(0, off, def->own));
656     }
657     if (off->type == EF_SECTOR) {
658         getsect(off->x, off->y, &sect);
659         sectdamage(&sect, dam, 0);
660         putsect(&sect);
661         pr("Enemy fleet at %s does %d damage to %s\n",
662            xyas(def->x, def->y, player->cnum), dam, prcom(0, off));
663     } else if (off->type == EF_SHIP) {
664         getship(off->shp_uid, &ship);
665         shipdamage(&ship, dam);
666         putship(off->shp_uid, &ship);
667         if (def->own && ship.shp_effic < SHIP_MINEFF) {
668             wu(0, def->own, "%s sunk!\n", pr_com(0, off, def->own));
669             nreport(player->cnum, N_SHP_LOSE, def->own, 1);
670         }
671     }
672     if (att_get_combat(off, 0) < 0)
673         return abort_attack();
674     return 0;
675 }
676
677 /* The attack is valid.  Tell the attacker about what they're going to hit */
678
679 int
680 att_show(struct combat *def)
681 {
682     /* Note that we tell the player about the treaty BEFORE we tell them
683        about the item.  If we didn't, then they gain free information */
684     if (def->type == EF_SECTOR) {
685         if (!trechk(player->cnum, def->own, LANATT))
686             return abort_attack();
687         pr("%s is a %d%% %s %s with approximately %d military.\n",
688            xyas(def->x, def->y, player->cnum),
689            roundintby((int)def->eff, 10),
690            cname(def->own), def->sct_dcp->d_name,
691            roundintby(def->troops, 10));
692         if (map_set(player->cnum, def->x, def->y, def->sct_dcp->d_mnem, 0))
693             writemap(player->cnum);
694     } else if (def->type == EF_SHIP || def->type == EF_LAND) {
695         if (def->type == EF_SHIP) {
696             if (!trechk(player->cnum, def->own, SEAATT))
697                 return abort_attack();
698         } else {
699             if (!trechk(player->cnum, def->own, LNDATT))
700                 return abort_attack();
701         }
702         pr("%s is about %d%% efficient and has approximately %d mil on board.\n", prcom(0, def), roundintby((int)def->eff, 10), roundintby(def->troops, 10));
703     }
704     /* Ok, everything is fine */
705     return 0;
706 }
707
708 /* Attack and assault ask the user which kind of support they want */
709
710 int
711 att_ask_support(int offset, int *fortp, int *shipp, int *landp,
712                 int *planep)
713 {
714     char buf[1024];
715     char *p;
716     *fortp = *shipp = *landp = *planep = 1;
717
718     if (player->argp[offset] != NULL) {
719         if ((player->argp[offset + 1] == NULL) ||
720             (player->argp[offset + 2] == NULL) ||
721             (player->argp[offset + 3] == NULL)) {
722             pr("If any support arguments are used, all must be!\n");
723             return RET_SYN;
724         }
725
726         *fortp = *shipp = 0;
727         *landp = *planep = 0;
728
729         if (!(p = getstarg(player->argp[offset], "Use fort support? ",
730                            buf)))
731             return RET_SYN;
732
733         if ((*p == 'y') || (*p == 'Y'))
734             *fortp = 1;
735
736         if (!(p = getstarg(player->argp[offset + 1], "Use ship support? ",
737                            buf)))
738             return RET_SYN;
739
740         if ((*p == 'y') || (*p == 'Y'))
741             *shipp = 1;
742
743         if (!(p = getstarg(player->argp[offset + 2], "Use land support? ",
744                            buf)))
745             return RET_SYN;
746
747         if ((*p == 'y') || (*p == 'Y'))
748             *landp = 1;
749
750         if (!(p = getstarg(player->argp[offset + 3], "Use plane support? ",
751                            buf)))
752             return RET_SYN;
753
754         if ((*p == 'y') || (*p == 'Y'))
755             *planep = 1;
756     }
757     return RET_OK;
758 }
759
760 /*
761  * Attack, assault, and board ask the attacker what they'd like to attack
762  * with.  This includes mil and land units from each "off" object.  Note that
763  * after each sub-prompt, we check to make sure that the attack is still
764  * valid, and if it's not, then we abort the attack.
765  */
766
767 int
768 att_ask_offense(int combat_mode, struct combat *off, struct combat *def,
769                 struct emp_qelem *olist, int *a_spyp, int *a_engineerp)
770 {
771     int n;
772     char land_answer[256];
773
774     emp_initque(olist);
775     if (att_abort(combat_mode, off, def))
776         return 0;
777     memset(land_answer, 0, sizeof(land_answer));
778     for (n = 0; n <= off->last; ++n) {
779         off[n].troops = ask_off(combat_mode, off + n, def);
780         if (att_abort(combat_mode, off, def))
781             return 0;
782         ask_olist(combat_mode, off + n, def, olist, land_answer,
783                   a_spyp, a_engineerp);
784         if (att_abort(combat_mode, off, def))
785             return 0;
786     }
787     return 0;
788 }
789
790 /*
791  * Return path cost for ATTACKER to enter sector given by DEF.
792  * MOBTYPE is a mobility type accepted by sector_mcost().
793  */
794 static double
795 att_mobcost(natid attacker, struct combat *def, int mobtype)
796 {
797     struct sctstr sect;
798     int ok;
799
800     if (CANT_HAPPEN(def->type != EF_SECTOR))
801         return -1.0;
802     ok = getsect(def->x, def->y, &sect);
803     if (CANT_HAPPEN(!ok))
804         return -1.0;
805
806     /*
807      * We want the cost to move/march into the sector.  If we just
808      * called sector_mcost(), we'd get the defender's cost.  The
809      * attacker's cost is higher unless he's the old-owner.  Note: if
810      * there are no civilians, a victorious attacker will become the
811      * old-owner.  But he isn't now.
812      */
813     sect.sct_own = attacker;
814     sect.sct_mobil = 0;
815     return sector_mcost(&sect, mobtype);
816 }
817
818 /* How many mil is off allowed to attack with when it attacks def? */
819
820 static int
821 get_mob_support(int combat_mode, struct combat *off, struct combat *def)
822 {
823     int mob_support;
824     double mobcost;
825
826     switch (combat_mode) {
827     case A_ATTACK:
828         mobcost = att_mobcost(off->own, def, MOB_MOVE);
829         if (mobcost < 0 || off->mob <= 0)
830             return 0;
831         mob_support = off->mob / mobcost;
832         if (mob_support < off->troops)
833             pr("Sector %s has %d mobility which can only support %d mil,\n",
834                xyas(off->x, off->y, player->cnum), off->mob, mob_support);
835         else
836             mob_support = off->troops;
837         return mob_support;
838     case A_ASSAULT:
839         if (def->own != player->cnum && def->mil) {
840             if (off->shp_mcp->m_flags & M_SEMILAND)
841                 return off->troops / 4;
842             else if (!(off->shp_mcp->m_flags & M_LAND))
843                 return off->troops / 10;
844         }
845         break;
846     case A_BOARD:
847         if (off->type == EF_SECTOR && off->mob <= 0)
848             return 0;
849         mob_support = def->shp_mcp->m_item[I_MILIT];
850         if (mob_support < off->troops)
851             pr("The size of the ship you are trying to board limits your party to %d mil,\n", mob_support);
852         else
853             mob_support = off->troops;
854         return mob_support;
855     case A_LBOARD:
856         if (off->mob <= 0)
857             return 0;
858         if (def->lnd_lcp->l_flags & L_SPY)
859             return 1;
860         mob_support = def->lnd_lcp->l_item[I_MILIT];
861         if (mob_support < off->troops)
862             pr("The size of the unit you are trying to board limits your party to %d mil,\n", mob_support);
863         else
864             mob_support = off->troops;
865         return mob_support;
866     }
867     return off->troops;
868 }
869
870 /*
871  * If the attacker decides to go through with the attack, then the
872  * sectors/ships they are attacking with may be charged some mobility.
873  * This is where that amount of mobility is calculated.  It is actually
874  * subtracted "for real" from the object's mobility in put_combat().
875  */
876
877 static void
878 calc_mobcost(int combat_mode, struct combat *off, struct combat *def,
879              int attacking_mil)
880 {
881     struct shpstr ship;
882
883     if (!attacking_mil)
884         return;
885     switch (combat_mode) {
886     case A_ATTACK:
887         off->mobcost += MAX(1,
888                             (int)(attacking_mil
889                                   * att_mobcost(off->own, def, MOB_MOVE)));
890         break;
891     case A_LBOARD:
892         off->mobcost += MAX(1, attacking_mil / 5);
893         break;
894     case A_BOARD:
895         switch (off->type) {
896         case EF_SECTOR:
897             off->mobcost += MAX(1, attacking_mil / 5);
898             break;
899         case EF_SHIP:
900             /* the 2 in the formula below is a fudge factor */
901             getship(def->shp_uid, &ship);
902             off->mobcost += (def->eff / 100) * (ship.shp_speed / 2);
903         }
904     }
905 }
906
907 /* How many mil to we want to attack from off against def? */
908
909 static int
910 ask_off(int combat_mode, struct combat *off, struct combat *def)
911 {
912     int attacking_mil;
913     int mob_support;
914     char prompt[512];
915
916     if (att_get_combat(off, 0) <= 0)
917         return 0;
918     if ((off->type == EF_SECTOR) && (off->own != player->cnum))
919         return 0;
920     if ((mob_support = get_mob_support(combat_mode, off, def)) <= 0)
921         return 0;
922     if (off->type == EF_SECTOR) {
923         if (off->own != player->cnum)
924             return 0;
925         sprintf(prompt, "Number of mil from %s at %s (max %d) : ",
926                 off->sct_dcp->d_name,
927                 xyas(off->x, off->y, player->cnum), mob_support);
928     } else {
929         sprintf(prompt, "Number of mil from %s (max %d) : ",
930                 prcom(0, off), mob_support);
931     }
932     if ((attacking_mil = onearg(0, prompt)) < 0)
933         abort_attack();
934     if (att_abort(combat_mode, off, def))
935         return 0;
936     if (att_get_combat(off, 0) <= 0)
937         return 0;
938     if ((attacking_mil =
939          MIN(attacking_mil, MIN(mob_support, off->troops))) <= 0)
940         return 0;
941
942     calc_mobcost(combat_mode, off, def, attacking_mil);
943     return attacking_mil;
944 }
945
946 /*
947  * Which units would you like to attack with or move in with [ynYNq?]
948  */
949
950 static char
951 att_prompt(char *prompt, char army)
952 {
953     char buf[1024];
954     char *p;
955
956     if (!army)
957         army = '~';
958     for (;;) {
959         p = getstring(prompt, buf);
960         if (player->aborted || (p && *p == 'q')) {
961             abort_attack();
962             return 'N';
963         }
964         if (!p || !*p)
965             return 'n';
966         if (tolower(*p) == 'y' || tolower(*p) == 'n')
967             return *p;
968         pr("y - yes this unit\n"
969            "n - no this unit\n"
970            "Y - yes to all units in army '%c'\n"
971            "N - no to all units in army '%c'\n"
972            "q - quit\n? - this help message\n\n",
973            army, army);
974     }
975 }
976
977 /* Ask the attacker which units they want to attack/assault/board with */
978
979 static void
980 ask_olist(int combat_mode, struct combat *off, struct combat *def,
981           struct emp_qelem *olist, char *land_answer, int *a_spyp,
982           int *a_engineerp)
983 {
984     struct nstr_item ni;
985     struct lndstr land;
986     double mobcost;
987     struct llist *llp;
988     struct lchrstr *lcp;
989     double att_val;
990     int count = 0;
991     int maxland = 0;
992     int first_time = 1;
993     char prompt[512];
994
995     if (def->type == EF_LAND)
996         return;
997     if (def->type == EF_SHIP)
998         maxland = def->shp_mcp->m_nland;
999
1000     snxtitem_xy(&ni, EF_LAND, off->x, off->y);
1001     while (nxtitem(&ni, &land)) {
1002         if (land.lnd_own != player->cnum)
1003             continue;
1004         if (land.lnd_effic < LAND_MINEFF)
1005             continue;
1006         if (land_answer[(int)land.lnd_army] == 'N')
1007             continue;
1008         if (!lnd_can_attack(&land))
1009             continue;
1010         lcp = &lchr[(int)land.lnd_type];
1011
1012         if (def->type == EF_SHIP && !maxland) {
1013             pr("Land units are not able to board this kind of ship\n");
1014             return;
1015         }
1016         if (land.lnd_mobil <= 0) {
1017             pr("%s is out of mobility, and cannot %s\n",
1018                prland(&land), att_mode[combat_mode]);
1019             continue;
1020         }
1021
1022         if (opt_MARKET) {
1023             if (ontradingblock(EF_LAND, &land)) {
1024                 pr("%s is on the trading block, and cannot %s\n",
1025                    prland(&land), att_mode[combat_mode]);
1026                 continue;
1027             }
1028         }
1029
1030         if (off->type == EF_SECTOR && land.lnd_ship >= 0) {
1031             pr("%s is on ship #%d, and cannot %s\n",
1032                prland(&land), land.lnd_ship, att_mode[combat_mode]);
1033             continue;
1034         } else if (off->type == EF_SHIP) {
1035             if (land.lnd_ship != off->shp_uid)
1036                 continue;
1037         } else if (land.lnd_land >= 0) {
1038             pr("%s is on unit #%d, and cannot %s\n",
1039                prland(&land), land.lnd_land, att_mode[combat_mode]);
1040             continue;
1041         }
1042         switch (combat_mode) {
1043         case A_ATTACK:
1044             /*
1045              * We used to let land units attack only if they have the
1046              * mobility consumed by the attack, not counting combat
1047              * and moving in to occupy.  Making sure your land units
1048              * reach attack positions with enough mobility left is a
1049              * pain in the neck.  We now require positive mobility,
1050              * just like for marching.  Except we don't allow rushing
1051              * of high-mobility sectors (mountains): for those we
1052              * still require attack mobility.
1053              */
1054             mobcost = att_mobcost(off->own, def, lnd_mobtype(&land));
1055             if (mobcost < 1.0) {
1056                 if (land.lnd_mobil <= 0) {
1057                     pr("%s is out of mobility\n", prland(&land));
1058                     continue;
1059                 }
1060             } else {
1061                 mobcost = lnd_pathcost(&land, mobcost);
1062                 if (land.lnd_mobil < mobcost) {
1063                     pr("%s does not have enough mobility (%d needed)\n",
1064                        prland(&land), (int)ceil(mobcost));
1065                     continue;
1066                 }
1067             }
1068             break;
1069         case A_ASSAULT:
1070         case A_BOARD:
1071             mobcost = 0;
1072             if (!(lcp->l_flags & L_ASSAULT))
1073                 continue;
1074             break;
1075         default:
1076             CANT_REACH();
1077             return;
1078         }
1079         att_val = attack_val(combat_mode, &land);
1080         if (att_val < 1.0) {
1081             pr("%s has no offensive strength\n", prland(&land));
1082             continue;
1083         }
1084         resupply_all(&land);
1085         putland(land.lnd_uid, &land);
1086         if (!has_supply(&land)) {
1087             pr("%s is out of supply, and cannot %s\n",
1088                prland(&land), att_mode[combat_mode]);
1089             continue;
1090         }
1091         if (def->type == EF_SHIP && first_time) {
1092             first_time = 0;
1093             pr("You may board with a maximum of %d land units\n", maxland);
1094         }
1095         pr("%s has a base %s value of %.0f\n",
1096            prland(&land), att_mode[combat_mode], att_val);
1097         if (land_answer[(int)land.lnd_army] != 'Y') {
1098             sprintf(prompt,
1099                     "%s with %s %s (%c %d%%) [ynYNq?] ",
1100                     att_mode[combat_mode],
1101                     prland(&land),
1102                     prcom(1, off),
1103                     land.lnd_army ? land.lnd_army : '~',
1104                     land.lnd_effic);
1105             land_answer[(int)land.lnd_army] =
1106                 att_prompt(prompt, land.lnd_army);
1107             if (att_abort(combat_mode, off, def))
1108                 return;
1109             if (land_answer[(int)land.lnd_army] != 'y' &&
1110                 land_answer[(int)land.lnd_army] != 'Y')
1111                 continue;
1112         }
1113         if (!(llp = malloc(sizeof(struct llist)))) {
1114             logerror("Malloc failed in attack!\n");
1115             abort_attack();
1116             return;
1117         }
1118         memset(llp, 0, sizeof(struct llist));
1119         emp_insque(&llp->queue, olist);
1120         llp->mobil = mobcost;
1121         if (!get_land(combat_mode, def, land.lnd_uid, llp, 0))
1122             continue;
1123         if (lnd_spyval(&land) > *a_spyp)
1124             *a_spyp = lnd_spyval(&land);
1125         if (llp->lcp->l_flags & L_ENGINEER)
1126             ++*a_engineerp;
1127         if (def->type == EF_SHIP && ++count >= maxland)
1128             break;
1129     }
1130 }
1131
1132 /* What's the offense or defense multiplier? */
1133
1134 double
1135 att_combat_eff(struct combat *com)
1136 {
1137     double eff = 1.0;
1138     double str;
1139     struct shpstr ship;
1140
1141     if (com->type == EF_SECTOR) {
1142         eff = com->eff / 100.0;
1143         if (com->own == player->cnum) {
1144             str = com->sct_dcp->d_ostr;
1145             eff = 1.0 + ((str - 1.0) * eff);
1146         } else
1147             eff = sector_strength(getsectp(com->x, com->y));
1148     } else if (com->type == EF_SHIP && com->own != player->cnum) {
1149         getship(com->shp_uid, &ship);
1150         eff = 1.0 + ship.shp_armor / 100.0;
1151     }
1152     return eff;
1153 }
1154
1155 int
1156 att_get_offense(int combat_mode, struct combat *off,
1157                 struct emp_qelem *olist, struct combat *def)
1158 {
1159     int ototal;
1160
1161     /*
1162      * Get the attacker units & mil again in case they changed while the
1163      * attacker was answering sub-prompts.
1164      */
1165
1166     ototal = get_ototal(combat_mode, off, olist, 1.0, 1);
1167     if (att_empty_attack(combat_mode, ototal, def))
1168         return abort_attack();
1169     if (combat_mode == A_PARA)
1170         return ototal;
1171     pr("\n             Initial attack strength: %8d\n", ototal);
1172     return ototal;
1173 }
1174
1175 /* Get the defensive units and reacting units */
1176 int
1177 att_get_defense(struct emp_qelem *olist, struct combat *def,
1178                 struct emp_qelem *dlist, int a_spy, int ototal)
1179 {
1180     int d_spy = 0;
1181     struct emp_qelem *qp;
1182     struct llist *llp;
1183     int dtotal;
1184     int old_dtotal;
1185
1186     emp_initque(dlist);
1187     get_dlist(def, dlist, 0, &d_spy);
1188     dtotal = get_dtotal(def, dlist, 1.0, 0);
1189
1190     /*
1191      * Call in reacting units
1192      */
1193
1194     if (def->type == EF_SECTOR && def->sct_type != SCT_MOUNT)
1195         att_reacting_units(def, dlist, a_spy, &d_spy, ototal);
1196
1197     for (qp = olist->q_forw; qp != olist; qp = qp->q_forw) {
1198         llp = (struct llist *)qp;
1199         intelligence_report(def->own, &llp->land, d_spy,
1200                             "Scouts report attacking unit:");
1201     }
1202
1203     old_dtotal = dtotal;
1204     dtotal = get_dtotal(def, dlist, 1.0, 0);
1205     if (dtotal != old_dtotal)
1206         pr("Defense strength with reacting units: %8d\n", dtotal);
1207
1208     return dtotal;
1209 }
1210
1211 /* Get the defensive land units in the sector or on the ship */
1212
1213 static void
1214 get_dlist(struct combat *def, struct emp_qelem *list, int a_spy,
1215           int *d_spyp)
1216 {
1217     struct nstr_item ni;
1218     struct llist *llp;
1219     struct lndstr land;
1220
1221 /* In here is where you need to take out spies and trains from the defending
1222    lists.  Spies try to hide, trains get trapped and can be boarded. */
1223
1224     snxtitem_xy(&ni, EF_LAND, def->x, def->y);
1225     while (nxtitem(&ni, &land)) {
1226         if (!land.lnd_own)
1227             continue;
1228         if (land.lnd_own != def->own)
1229             continue;
1230         if (def->type == EF_SECTOR && land.lnd_ship >= 0)
1231             continue;
1232         if (def->type == EF_SECTOR && land.lnd_land >= 0)
1233             continue;
1234         if (def->type == EF_SHIP && land.lnd_ship != def->shp_uid)
1235             continue;
1236         if (def->type == EF_LAND && land.lnd_land != def->lnd_uid)
1237             continue;
1238         if (!list) {            /* Just estimating the enemy strength */
1239             intelligence_report(player->cnum, &land, a_spy,
1240                                 "Scouts report defending unit:");
1241             continue;
1242         }
1243         if (!(llp = malloc(sizeof(struct llist)))) {
1244             logerror("Malloc failed in attack!\n");
1245             abort_attack();
1246             return;
1247         }
1248         memset(llp, 0, sizeof(struct llist));
1249         emp_insque(&llp->queue, list);
1250         llp->supplied = has_supply(&land);
1251         if (!get_land(A_DEFEND, def, land.lnd_uid, llp, 1))
1252             continue;
1253         if (lnd_spyval(&land) > *d_spyp)
1254             *d_spyp = lnd_spyval(&land);
1255     }
1256 }
1257
1258 /* Calculate the total offensive strength */
1259
1260 static int
1261 get_ototal(int combat_mode, struct combat *off, struct emp_qelem *olist,
1262            double osupport, int check)
1263 {
1264     double ototal = 0.0;
1265     struct emp_qelem *qp, *next;
1266     struct llist *llp;
1267     int n, w;
1268
1269     /*
1270      * first, total the attacking mil
1271      */
1272
1273     for (n = 0; n <= off->last; ++n) {
1274         if (off[n].type == EF_BAD || (check &&
1275                                       att_get_combat(&off[n], 0) <= 0))
1276             continue;
1277         ototal += off[n].troops * att_combat_eff(off + n);
1278     }
1279
1280     /*
1281      * next, add in the attack_values of all
1282      * the attacking units
1283      */
1284
1285     for (qp = olist->q_forw; qp != olist; qp = next) {
1286         next = qp->q_forw;
1287         llp = (struct llist *)qp;
1288         if (check && !get_land(combat_mode, 0, llp->land.lnd_uid, llp, 0))
1289             continue;
1290         if (combat_mode == A_ATTACK) {
1291             w = -1;
1292             for (n = 0; n <= off->last; ++n) {
1293                 if (off[n].type == EF_BAD)
1294                     continue;
1295                 if ((off[n].x == llp->land.lnd_x) &&
1296                     (off[n].y == llp->land.lnd_y))
1297                     w = n;
1298             }
1299             if (w < 0) {
1300                 lnd_delete(llp, "is in a sector not owned by you");
1301                 continue;
1302             }
1303             ototal += attack_val(combat_mode, &llp->land) *
1304                 att_combat_eff(off + w);
1305         } else {
1306             ototal += attack_val(combat_mode, &llp->land);
1307         }
1308     }
1309     ototal *= osupport;
1310
1311     return ldround(ototal, 1);
1312 }
1313
1314 /* Calculate the total defensive strength */
1315
1316 static int
1317 get_dtotal(struct combat *def, struct emp_qelem *list, double dsupport,
1318            int check)
1319 {
1320     double dtotal = 0.0, eff = 1.0, d_unit;
1321     struct emp_qelem *qp, *next;
1322     struct llist *llp;
1323
1324     if (check && att_get_combat(def, 1) < 0)
1325         return 0;
1326     eff = att_combat_eff(def);
1327     dtotal = def->troops * eff;
1328
1329     /*
1330      * next, add in the defense_values of all
1331      * the defending non-retreating units
1332      */
1333
1334     for (qp = list->q_forw; qp != list; qp = next) {
1335         next = qp->q_forw;
1336         llp = (struct llist *)qp;
1337         if (check && !get_land(A_DEFEND, def, llp->land.lnd_uid, llp, 1))
1338             continue;
1339         d_unit = defense_val(&llp->land);
1340         if (!llp->supplied)
1341             d_unit /= 2.0;
1342         dtotal += d_unit * eff;
1343     }
1344
1345     dtotal *= dsupport;
1346
1347     return ldround(dtotal, 1);
1348 }
1349
1350 /*
1351  * This is the land unit integrity check.  Note that we don't print
1352  * warnings about victim land units because the attacker may not have seen them
1353  */
1354
1355 static int
1356 get_land(int combat_mode, struct combat *def, int uid, struct llist *llp,
1357          int victim_land)
1358 {
1359     struct lndstr *lp = &llp->land;
1360     char buf[512];
1361
1362     getland(uid, lp);
1363
1364     if (!llp->lcp) {            /* first time */
1365         llp->x = llp->land.lnd_x;
1366         llp->y = llp->land.lnd_y;
1367         llp->lcp = &lchr[(int)llp->land.lnd_type];
1368     } else {                    /* not first time */
1369         if (lp->lnd_effic < LAND_MINEFF) {
1370             sprintf(buf, "was destroyed and is no longer a part of the %s",
1371                     att_mode[combat_mode]);
1372             lnd_delete(llp, buf);
1373             return 0;
1374         }
1375         if (victim_land) {
1376             if (lp->lnd_x != def->x || lp->lnd_y != def->y) {
1377                 lnd_delete(llp,
1378                            "left to go fight another battle and is no longer a part of the defense");
1379                 return 0;
1380             }
1381         } else {
1382             if (lp->lnd_own != player->cnum) {
1383                 sprintf(buf,
1384                         "was destroyed and is no longer a part of the %s",
1385                         att_mode[combat_mode]);
1386                 lnd_delete(llp, buf);
1387                 return 0;
1388             }
1389             if (lp->lnd_x != llp->x || lp->lnd_y != llp->y) {
1390                 sprintf(buf,
1391                         "left to fight another battle and is no longer a part of the %s",
1392                         att_mode[combat_mode]);
1393                 lnd_delete(llp, buf);
1394                 return 0;
1395             }
1396             if (lp->lnd_effic < llp->eff) {
1397                 sprintf(buf, "damaged from %d%% to %d%%",
1398                         llp->eff, lp->lnd_effic);
1399                 lnd_print(llp, buf);
1400             }
1401         }
1402     }
1403     llp->eff = llp->land.lnd_effic;
1404
1405     return 1;
1406 }
1407
1408 /*
1409  * Put the land unit on the disk.  If there was some mobility cost, then
1410  * subtract it from the units mobility.  Note that this works the same way
1411  * as sectors & ships in that no mobility is actually taken until the attacker
1412  * has committed to attacking.
1413  */
1414
1415 static void
1416 kill_land(struct emp_qelem *list)
1417 {
1418     struct emp_qelem *qp, *next;
1419     struct llist *llp;
1420
1421     for (qp = list->q_forw; qp != list; qp = next) {
1422         next = qp->q_forw;
1423         llp = (struct llist *)qp;
1424         if (llp->land.lnd_ship >= 0) {
1425             llp->land.lnd_effic = 0;
1426             lnd_delete(llp, "cannot return to the ship, and dies!");
1427         }
1428     }
1429 }
1430
1431 static void
1432 att_infect_units(struct emp_qelem *list, int plague)
1433 {
1434     struct emp_qelem *qp, *next;
1435     struct llist *llp;
1436
1437     if (!plague)
1438         return;
1439     for (qp = list->q_forw; qp != list; qp = next) {
1440         next = qp->q_forw;
1441         llp = (struct llist *)qp;
1442         if (llp->land.lnd_pstage == PLG_HEALTHY)
1443             llp->land.lnd_pstage = PLG_EXPOSED;
1444     }
1445 }
1446
1447 static void
1448 put_land(struct emp_qelem *list)
1449 {
1450     struct emp_qelem *qp, *next;
1451     struct llist *llp;
1452
1453     for (qp = list->q_forw; qp != list; qp = next) {
1454         next = qp->q_forw;
1455         llp = (struct llist *)qp;
1456         llp->land.lnd_mission = 0;
1457         llp->land.lnd_harden = 0;
1458         llp->land.lnd_mobil -= (int)llp->mobil;
1459         llp->mobil = 0.0;
1460         putland(llp->land.lnd_uid, &llp->land);
1461         if (llp->land.lnd_own != player->cnum) {
1462             emp_remque((struct emp_qelem *)llp);
1463             free(llp);
1464         } else
1465             get_land(A_ATTACK, 0, llp->land.lnd_uid, llp, 0);
1466     }
1467 }
1468
1469 /*
1470  * Keep sending in reinforcements until it looks like we're going to win.
1471  * Note that the "strength" command also calls this routine.
1472  */
1473
1474 double
1475 att_reacting_units(struct combat *def, struct emp_qelem *list, int a_spy,
1476                    int *d_spyp, int ototal)
1477 {
1478     struct nstr_item ni;
1479     struct lndstr land;
1480     struct sctstr sect, dsect;
1481     struct llist *llp;
1482     int dtotal;
1483     double new_land = 0;
1484     double mobcost;
1485     double pathcost;
1486     int dist;
1487     int radius;
1488     int origx, origy;
1489     double eff = att_combat_eff(def);
1490     char buf[1024];
1491
1492     if (list)
1493         dtotal = get_dtotal(def, list, 1.0, 1);
1494     else
1495         dtotal = 0;
1496     snxtitem_all(&ni, EF_LAND);
1497     while (nxtitem(&ni, &land) && dtotal + new_land * eff < 1.2 * ototal) {
1498         if (!land.lnd_own)
1499             continue;
1500         if (!land.lnd_rad_max)
1501             continue;
1502         if ((land.lnd_x == def->x) && (land.lnd_y == def->y))
1503             continue;
1504         if (land.lnd_own != def->own)
1505             continue;
1506         if (land.lnd_ship >= 0)
1507             continue;
1508         if (land.lnd_land >= 0)
1509             continue;
1510         if (defense_val(&land) < 1.0)
1511             continue;
1512         if (!lnd_can_attack(&land))
1513             continue;
1514
1515         /* Only supplied units can react */
1516         if (!has_supply(&land))
1517             continue;
1518
1519         dist = mapdist(land.lnd_x, land.lnd_y, def->x, def->y);
1520
1521         getsect(land.lnd_x, land.lnd_y, &sect);
1522         /* Units on efficient headquarters can react 1 farther */
1523         if ((sect.sct_type == SCT_HEADQ) && (sect.sct_effic >= 60))
1524             radius = land.lnd_rad_max + 1;
1525         else
1526             radius = land.lnd_rad_max;
1527
1528         if (land.lnd_mission == MI_RESERVE)
1529             radius += 2;
1530
1531         if (dist > radius)
1532             continue;
1533
1534         getsect(def->x, def->y, &dsect);
1535         if (!BestLandPath(buf, &sect, &dsect, &pathcost,
1536                           lnd_mobtype(&land)))
1537             continue;
1538
1539         mobcost = lnd_pathcost(&land, pathcost);
1540         if (land.lnd_mobil < mobcost)
1541             continue;
1542
1543         new_land += defense_val(&land);
1544
1545         if (!list)              /* we are in the "strength" command */
1546             continue;
1547
1548         /* move to defending sector */
1549         land.lnd_mobil -= ldround(mobcost, 1);
1550         origx = land.lnd_x;
1551         origy = land.lnd_y;
1552         land.lnd_x = def->x;
1553         land.lnd_y = def->y;
1554         putland(land.lnd_uid, &land);
1555         wu(0, land.lnd_own, "%s reacts to %s.\n",
1556            prland(&land), xyas(land.lnd_x, land.lnd_y, land.lnd_own));
1557
1558         llp = malloc(sizeof(struct llist));
1559
1560         memset(llp, 0, sizeof(struct llist));
1561         llp->supplied = 1;
1562         llp->x = origx;
1563         llp->y = origy;
1564         llp->lcp = &lchr[(int)land.lnd_type];
1565         llp->land = land;
1566         emp_insque(&llp->queue, list);
1567         if (lnd_spyval(&land) > *d_spyp)
1568             *d_spyp = lnd_spyval(&land);
1569
1570         intelligence_report(player->cnum, &land, a_spy,
1571                             "Scouts sight reacting enemy unit:");
1572     }
1573     return new_land;
1574 }
1575
1576 /* Pop off shells and fly bombing missions to get your attack multiplier up */
1577
1578 static double
1579 get_osupport(char *outs, struct combat *def, int fort_sup, int ship_sup,
1580              int land_sup, int plane_sup)
1581 {
1582     double osupport = 1.0;
1583     int dam;
1584     double af, as, au, ap;
1585
1586     af = as = au = ap = 0.0;
1587     if (fort_sup) {
1588         dam = dd(def->own, player->cnum, def->x, def->y, 0, 0);
1589         af = dam / 100.0;
1590         osupport += af;
1591     }
1592     if (ship_sup) {
1593         dam = sd(def->own, player->cnum, def->x, def->y, 0, 0, 0);
1594
1595         as = dam / 100.0;
1596         osupport += as;
1597     }
1598
1599     if (land_sup) {
1600         dam = lnd_support(def->own, player->cnum, def->x, def->y, 0);
1601         au = dam / 100.0;
1602         osupport += au;
1603     }
1604
1605     if (plane_sup) {
1606         dam = off_support(def->x, def->y, def->own, player->cnum);
1607         ap = dam / 100.0;
1608         osupport += ap;
1609     }
1610     sprintf(outs, "attacker\t%1.2f\t%1.2f\t%1.2f\t%1.2f\n", af, as, au,
1611             ap);
1612     return osupport;
1613 }
1614
1615 /* Pop off shells and fly bombing missions to get your defense multiplier up */
1616
1617 static double
1618 get_dsupport(char *outs, struct emp_qelem *list, struct combat *def,
1619              int ototal, int dtotal)
1620 {
1621     double dsupport = 1.0;
1622     int dam;
1623     double df, ds, du, dp;
1624     int good = 0;
1625
1626     df = ds = du = dp = 0.0;
1627     if (dtotal < 0.1 * ototal) {
1628         good = -1;
1629     } else if (dtotal >= 1.2 * ototal) {
1630         good = 1;
1631     } else {
1632         dam = dd(player->cnum, def->own, def->x, def->y, 0, 1);
1633         df = dam / 100.0;
1634         dsupport += df;
1635
1636         dtotal = get_dtotal(def, list, dsupport, 0);
1637         if (dtotal < 1.2 * ototal) {
1638             dam = sd(player->cnum, def->own, def->x, def->y, 0, 1, 0);
1639             ds = dam / 100.0;
1640             dsupport += ds;
1641             dtotal = get_dtotal(def, list, dsupport, 0);
1642         }
1643         if (dtotal < 1.2 * ototal) {
1644             dam = lnd_support(player->cnum, def->own, def->x, def->y, 1);
1645             du = dam / 100.0;
1646             dsupport += du;
1647             dtotal = get_dtotal(def, list, dsupport, 1);
1648         }
1649         if (dtotal < 1.2 * ototal) {
1650             dam = def_support(def->x, def->y, player->cnum, def->own);
1651             dp = dam / 100.0;
1652             dsupport += dp;
1653         }
1654     }
1655     if (good)
1656         *outs = '\0';
1657     else
1658         sprintf(outs, "defender\t%1.2f\t%1.2f\t%1.2f\t%1.2f\n\n", df, ds,
1659                 du, dp);
1660     if (def->own) {
1661         if (good < 0)
1662             wu(0, def->own,
1663                "\nOdds are bad for us...support cancelled.\n\n");
1664         else if (good > 0)
1665             wu(0, def->own,
1666                "\nOdds are good for us...support cancelled.\n\n");
1667     }
1668     return dsupport;
1669 }
1670
1671 /*
1672  * Land mines add to the defense multiplier.  If the attacker has engineers
1673  * then this multiplier is cut in half.
1674  */
1675
1676 static double
1677 get_mine_dsupport(struct combat *def, int a_engineer)
1678 {
1679     int mines;
1680     struct sctstr sect;
1681
1682     getsect(def->x, def->y, &sect);
1683
1684     if (sect.sct_oldown != player->cnum) {
1685         mines = MIN(sect.sct_mines, 20);
1686         if (a_engineer)
1687             mines = ldround(mines / 2.0, 1);
1688         if (mines > 0) {
1689             if (def->own)
1690                 wu(0, def->own, "Defending mines add %1.2f\n",
1691                    mines * 0.02);
1692             pr("Defending mines add %1.2f\n", mines * 0.02);
1693             return mines * 0.02;
1694         }
1695     }
1696     return 0.0;
1697 }
1698
1699 /* Get the offensive and defensive support */
1700 int
1701 att_get_support(int combat_mode, int ofort, int oship, int oland,
1702                 int oplane, struct emp_qelem *olist, struct combat *off,
1703                 struct emp_qelem *dlist, struct combat *def,
1704                 double *osupportp, double *dsupportp, int a_engineer)
1705 {
1706     int ototal, dtotal;
1707     char osupports[512];
1708     char dsupports[512];
1709
1710     if (combat_mode == A_PARA)
1711         *osupports = '\0';
1712     else
1713         *osupportp = get_osupport(osupports, def,
1714                                   ofort, oship, oland, oplane);
1715
1716     /*
1717      * I need to put a 1 at the end of the next four total_stren calls
1718      * because units & mil may have been damaged by collateral damage or
1719      * nuclear warheads from the offensive & defensive support.
1720      */
1721
1722     ototal = get_ototal(combat_mode, off, olist, *osupportp, 1);
1723     if (att_empty_attack(combat_mode, ototal, def))
1724         return abort_attack();
1725     dtotal = get_dtotal(def, dlist, *dsupportp, 1);
1726
1727     /*
1728      * Calculate defensive support.  If odds are too good or too bad
1729      * then don't call in support.
1730      */
1731
1732     *dsupportp = get_dsupport(dsupports, dlist, def, ototal, dtotal);
1733     ototal = get_ototal(combat_mode, off, olist, *osupportp, 1);
1734     if (att_empty_attack(combat_mode, ototal, def))
1735         return abort_attack();
1736
1737     if ((*osupports || *dsupports) &&
1738         (*osupportp != 1.0 || *dsupportp != 1.0)) {
1739         pr("\n\t\tsupport values\n");
1740         pr("\t\tforts\tships\tunits\tplanes\n");
1741         if (*osupportp != 1.0)
1742             pr("%s", osupports);
1743         if (*dsupportp != 1.0)
1744             pr("%s", dsupports);
1745         if (def->own) {
1746             wu(0, def->own, "\n\t\tsupport values\n");
1747             wu(0, def->own, "\t\tforts\tships\tunits\tplanes\n");
1748             if (*osupportp != 1.0)
1749                 wu(0, def->own, "%s", osupports);
1750             if (*dsupportp != 1.0)
1751                 wu(0, def->own, "%s", dsupports);
1752         }
1753     }
1754
1755     dtotal = get_dtotal(def, dlist, *dsupportp, 1);
1756     if (dtotal && def->type == EF_SECTOR)
1757         *dsupportp += get_mine_dsupport(def, a_engineer);
1758     return 0;
1759 }
1760
1761 /* How many two-legged bipeds are in this combat force? */
1762
1763 static int
1764 count_bodies(struct combat *off, struct emp_qelem *list)
1765 {
1766     int n;
1767     int bodies = 0;
1768     struct emp_qelem *qp;
1769     struct llist *llp;
1770
1771     for (n = 0; n <= off->last; ++n)
1772         bodies += off[n].troops;
1773     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
1774         llp = (struct llist *)qp;
1775         bodies += llp->land.lnd_item[I_MILIT];
1776     }
1777     return bodies;
1778 }
1779
1780 /* This is where the fighting actually occurs. */
1781
1782 int
1783 att_fight(int combat_mode, struct combat *off, struct emp_qelem *olist,
1784           double osupport, struct combat *def, struct emp_qelem *dlist,
1785           double dsupport)
1786 {
1787     int success = 0;
1788     int a_cas = 0;              /* Casualty counts */
1789     int d_cas = 0;
1790     int ototal;                 /* total attacking strength */
1791     int dtotal;                 /* total defending strength */
1792     int a_bodies;               /* total attacking mil (incl. mil in units) */
1793     int d_bodies;               /* total defending mil (incl. mil in units) */
1794     int d_mil;
1795     int a_troops[6];
1796     int n;
1797     int news_item;
1798     int recalctime;
1799     double odds;
1800     int newmob;
1801     char *action;
1802
1803     ototal = get_ototal(combat_mode, off, olist, osupport,
1804                         combat_mode != A_PARA);
1805     dtotal = get_dtotal(def, dlist, dsupport, 0);
1806     if (!dtotal)
1807         success = 1;
1808
1809     a_bodies = count_bodies(off, olist);
1810     d_bodies = count_bodies(def, dlist);
1811     d_mil = def->troops;
1812     for (n = 0; n <= off->last; ++n)
1813         if (off[n].type == EF_BAD)
1814             a_troops[n] = 0;
1815         else
1816             a_troops[n] = off[n].troops;
1817
1818     /* This switch is required to get the spacing right */
1819     switch (combat_mode) {
1820     case A_ATTACK:
1821         pr("               Final attack strength: %8d\n", ototal);
1822         break;
1823     case A_ASSAULT:
1824         pr("              Final assault strength: %8d\n", ototal);
1825         break;
1826     case A_PARA:
1827         if (def->sct_type == SCT_MOUNT ||
1828             def->sct_type == SCT_WATER ||
1829             def->sct_type == SCT_CAPIT ||
1830             def->sct_type == SCT_FORTR || def->sct_type == SCT_WASTE) {
1831             pr("You can't air-assault a %s sector!\n",
1832                def->sct_dcp->d_name);
1833             a_cas = a_bodies;
1834             off[0].troops = 0;
1835             ototal = get_ototal(A_PARA, off, olist, osupport, 0);
1836         }
1837         pr("          Final air-assault strength: %8d\n", ototal);
1838         break;
1839     case A_BOARD:
1840     case A_LBOARD:
1841         pr("                Final board strength: %8d\n", ototal);
1842     }
1843
1844
1845     pr("              Final defense strength: %8d\n", dtotal);
1846     odds = att_calcodds(ototal, dtotal);
1847     pr("                          Final odds: %8d%%\n", (int)(odds * 100));
1848
1849     /* spread the plague */
1850     if (combat_mode != A_PARA) {
1851         if (!def->plague)
1852             for (n = 0; n <= off->last; ++n)
1853                 if (off[n].type != EF_BAD)
1854                     def->plague |= off[n].plague;
1855         for (n = 0; n <= off->last; ++n)
1856             if (off[n].type != EF_BAD)
1857                 off[n].plague |= def->plague;
1858     }
1859     att_infect_units(olist, off->plague);
1860     att_infect_units(dlist, def->plague);
1861
1862     /*
1863      * Fighting is slightly random.  There is always that last little 
1864      * effort you see people put in.  Or the stray bullet that takes out
1865      * an officer and the rest go into chaos.  Things like that.
1866      * Thus, we have added a very slight random factor that will sometimes
1867      * allow the little guy to win. We modify the odds a little
1868      * (either +- 5%) to account for this randomness.  We also only
1869      * recalculate the odds every 8-50 casualties, not every cacsualty,
1870      * since a single dead guy normally wouldn't cause a commander to
1871      * rethink his strategies, but 50 dead guys might.
1872      */
1873     odds += (random() % 11 - 5) / 100.0;
1874     if (odds < 0.0)
1875         odds = 0.1;
1876     if (odds > 1.0)
1877         odds = 1.0;
1878     recalctime = 8 + (random() % 43);
1879     while (!success && ototal) {
1880         if (chance(odds)) {
1881             pr("!");
1882             d_cas += take_casualty(A_DEFEND, def, dlist);
1883             dtotal = get_dtotal(def, dlist, dsupport, 0);
1884             if (!dtotal)
1885                 ++success;
1886         } else {
1887             pr("@");
1888             a_cas += take_casualty(combat_mode, off, olist);
1889             ototal = get_ototal(combat_mode, off, olist, osupport, 0);
1890         }
1891         if (((a_cas + d_cas) % 70) == 69)
1892             pr("\n");
1893         if (recalctime-- <= 0) {
1894             recalctime = 8 + (random() % 43);
1895             odds = att_calcodds(ototal, dtotal);
1896             odds += (random() % 11 - 5) / 100.0;
1897             if (odds < 0.0)
1898                 odds = 0.1;
1899             if (odds > 1.0)
1900                 odds = 1.0;
1901         }
1902     }
1903     pr("\n");
1904     /* update defense mobility & mil */
1905     if (success)
1906         def->mil = 0;
1907     else {
1908         if (def->type == EF_SECTOR && d_mil && d_cas) {
1909             if (def->mob < 0)
1910                 def->mobcost = 0;
1911             else {
1912                 newmob = damage(def->mob, 100 * d_cas / d_mil);
1913                 def->mobcost = MIN(20, def->mob - newmob);
1914             }
1915         }
1916         def->mil = def->troops;
1917     }
1918
1919     /* update attack mobility & mil */
1920     for (n = 0; n <= off->last; ++n)
1921         if (off[n].type != EF_BAD && off[n].troops < a_troops[n]) {
1922             if (off[n].type == EF_SECTOR && off[n].mil) {
1923                 if (!CANT_HAPPEN(off[n].mob < 0)) {
1924                     newmob = damage(off[n].mob,
1925                                     100 * (a_troops[n] - off[n].troops)
1926                                     / off[n].mil);
1927                     off[n].mobcost += MIN(20, off[n].mob - newmob);
1928                 }
1929             }
1930             off[n].mil -= a_troops[n] - off[n].troops;
1931         }
1932
1933     /* update land unit mobility */
1934     if (d_bodies && d_cas)
1935         lnd_takemob(dlist, (double)d_cas / d_bodies);
1936     if (a_bodies && a_cas)
1937         lnd_takemob(olist, (double)a_cas / a_bodies);
1938
1939     /* damage attacked sector */
1940     def->eff = effdamage(def->eff, (d_cas + a_cas) / 10);
1941
1942     pr("- Casualties -\n     Yours: %d\n", a_cas);
1943     pr("    Theirs: %d\n", d_cas);
1944     pr("Papershuffling ... %.1f B.T.U\n", (d_cas + a_cas) * 0.15);
1945     player->btused += (int)((d_cas + a_cas) * 0.015 + 0.5);
1946
1947     if (success) {
1948         switch (combat_mode) {
1949         case A_ATTACK:
1950             news_item = def->own ? N_WON_SECT : N_TOOK_UNOCC;
1951             pr("We have captured %s, sir!\n", prcom(0, def));
1952             action = "taking";
1953             break;
1954         case A_ASSAULT:
1955             news_item = def->own ? N_AWON_SECT : N_START_COL;
1956             pr("We have secured a beachhead at %s, sir!\n", prcom(0, def));
1957             action = "assaulting and taking";
1958             break;
1959         case A_PARA:
1960             news_item = def->own ? N_PWON_SECT : N_PARA_UNOCC;
1961             pr("We have captured %s, sir!\n", prcom(0, def));
1962             action = "air-assaulting and taking";
1963             break;
1964         case A_BOARD:
1965             news_item = N_BOARD_SHIP;
1966             pr("We have boarded %s, sir!\n", prcom(0, def));
1967             action = "boarding";
1968             break;
1969         case A_LBOARD:
1970             news_item = N_BOARD_LAND;
1971             pr("We have boarded %s, sir!\n", prcom(0, def));
1972             action = "boarding";
1973             break;
1974         default:
1975             CANT_REACH();
1976             news_item = 0;
1977             action = "defeating";
1978         }
1979     } else {
1980         switch (combat_mode) {
1981         case A_ATTACK:
1982             news_item = N_SCT_LOSE;
1983             pr("You have been defeated!\n");
1984             action = "attacking";
1985             break;
1986         case A_ASSAULT:
1987             news_item = N_ALOSE_SCT;
1988             pr("You have been defeated!\n");
1989             kill_land(olist);
1990             action = "trying to assault";
1991             break;
1992         case A_PARA:
1993             news_item = N_PLOSE_SCT;
1994             pr("All of your troops were destroyed\n");
1995             action = "trying to air-assault";
1996             break;
1997         case A_BOARD:
1998             news_item = N_SHP_LOSE;
1999             pr("You have been repelled\n");
2000             kill_land(olist);
2001             action = "trying to board";
2002             break;
2003         case A_LBOARD:
2004             news_item = N_LND_LOSE;
2005             pr("You have been repelled\n");
2006             kill_land(olist);
2007             action = "trying to board";
2008             break;
2009         default:
2010             CANT_REACH();
2011             news_item = 0;
2012             action = "fighting";
2013         }
2014     }
2015     nreport(player->cnum, news_item, def->own, 1);
2016     if (def->own) {
2017         wu(0, def->own,
2018            "%s (#%d) lost %d troops %s %s\nWe lost %d troops defending\n",
2019            cname(player->cnum), player->cnum, a_cas,
2020            action, pr_com(0, def, def->own), d_cas);
2021     }
2022
2023     send_reacting_units_home(dlist);
2024
2025     /* putland the defending land */
2026     lnd_put(dlist, 0);
2027
2028     /* putland the attacking land */
2029     put_land(olist);
2030
2031     /* put the victim sector/ship/land */
2032     if (!success || !take_def(combat_mode, olist, off, def))
2033         put_combat(def);
2034
2035     /* put the attacking sectors/ship */
2036     for (n = 0; n <= off->last; ++n)
2037         if (off[n].type != EF_BAD)
2038             put_combat(&off[n]);
2039
2040     if (!success)
2041         return 0;
2042
2043     switch (combat_mode) {
2044     case A_ATTACK:
2045         ask_move_in(off, olist, def);
2046
2047         /* put sectors again to get abandon warnings */
2048         for (n = 0; n <= off->last; ++n)
2049             if (off[n].type != EF_BAD)
2050                 put_combat(&off[n]);
2051         break;
2052     default:
2053         att_move_in_off(combat_mode, off, olist, def);
2054     }
2055     if (def->mil > 0)
2056         pr("%d of your troops now occupy %s\n", def->mil, prcom(0, def));
2057     return 1;
2058 }
2059
2060 /* What percentage of the combat forces going head-to-head are we? */
2061
2062 static double
2063 att_calcodds(int ototal, int dtotal)
2064 {
2065     double odds;
2066
2067     /* calculate odds */
2068     if (ototal <= 0)
2069         odds = 0.0;
2070     else if (dtotal <= 0)
2071         odds = 1.0;
2072     else
2073         odds = (double)ototal / (dtotal + ototal);
2074
2075     return odds;
2076 }
2077
2078 /* Here's where the dead soldiers get dragged off the battlefield */
2079
2080 static int
2081 take_casualty(int combat_mode, struct combat *off, struct emp_qelem *olist)
2082 {
2083     int to_take = CASUALTY_LUMP;
2084     int biggest_troops = 0, index = -1;
2085     int n, tot_troops = 0, biggest_mil, cas;
2086     struct emp_qelem *qp, *biggest;
2087     struct llist *llp;
2088
2089     for (n = 0; n <= off->last; ++n) {
2090         if (off[n].type != EF_BAD) {
2091             tot_troops += off[n].troops;
2092             if (off[n].troops > biggest_troops) {
2093                 biggest_troops = off[n].troops;
2094                 index = n;
2095             }
2096         }
2097     }
2098
2099     if (tot_troops)
2100         to_take -= tot_troops;
2101
2102     if (to_take >= 0) {
2103         for (n = 0; n <= off->last; ++n)
2104             if (off[n].type != EF_BAD)
2105                 off[n].troops = 0;
2106     } else {
2107         /*
2108          * They can all come off mil.  We rotate the casualties,
2109          * starting with the sector containing the most mil.
2110          */
2111         to_take = CASUALTY_LUMP;
2112         if (index < 0) {
2113             pr("ERROR: Tell the deity that you got the 'green librarian' error\n");
2114             index = 0;
2115         }
2116         while (to_take > 0) {
2117             for (n = index; n <= off->last && to_take; ++n) {
2118                 if (off[n].type != EF_BAD && off[n].troops > 0) {
2119                     --to_take;
2120                     --off[n].troops;
2121                 }
2122             }
2123             for (n = 0; n < index && to_take; ++n) {
2124                 if (off[n].type != EF_BAD && off[n].troops > 0) {
2125                     --to_take;
2126                     --off[n].troops;
2127                 }
2128             }
2129         }
2130         return CASUALTY_LUMP;
2131     }
2132
2133     if (QEMPTY(olist))
2134         return CASUALTY_LUMP - to_take;
2135
2136     /*
2137      *  Need to take some casualties from attacking units
2138      *  Procedure: find the biggest unit remaining (in
2139      *  terms of mil) and give it the casualties.
2140      */
2141     biggest = NULL;
2142     biggest_mil = -1;
2143     for (qp = olist->q_forw; qp != olist; qp = qp->q_forw) {
2144         llp = (struct llist *)qp;
2145
2146         if (llp->land.lnd_item[I_MILIT] > biggest_mil) {
2147             biggest_mil = llp->land.lnd_item[I_MILIT];
2148             biggest = qp;
2149         }
2150     }
2151     if (biggest == NULL)
2152         return CASUALTY_LUMP - to_take;
2153
2154     llp = (struct llist *)biggest;
2155     cas = lnd_take_casualty(combat_mode, llp, to_take);
2156     return CASUALTY_LUMP - (to_take - cas);
2157 }
2158
2159 /* Send reacting defense units back to where they came from (at no mob cost) */
2160
2161 static void
2162 send_reacting_units_home(struct emp_qelem *list)
2163 {
2164     struct emp_qelem *qp, *next;
2165     struct llist *llp;
2166     char buf[1024];
2167
2168     for (qp = list->q_forw; qp != list; qp = next) {
2169         next = qp->q_forw;
2170         llp = (struct llist *)qp;
2171         if ((llp->land.lnd_x != llp->x) || (llp->land.lnd_y != llp->y)) {
2172             sprintf(buf, "returns to %s",
2173                     xyas(llp->x, llp->y, llp->land.lnd_own));
2174             llp->land.lnd_x = llp->x;
2175             llp->land.lnd_y = llp->y;
2176             lnd_delete(llp, buf);
2177         }
2178     }
2179 }
2180
2181 /* Check for 0 offense strength.  This call will always preceed an abort */
2182
2183 int
2184 att_empty_attack(int combat_mode, int ototal, struct combat *def)
2185 {
2186     if (ototal <= 0) {
2187         if (def->own && player->cnum != def->own) {
2188             wu(0, def->own,
2189                "%s (#%d) considered %sing you @%s\n",
2190                cname(player->cnum), player->cnum,
2191                att_mode[combat_mode], xyas(def->x, def->y, def->own));
2192         }
2193         pr("No troops for %s...\n", att_mode[combat_mode]);
2194         return 1;
2195     }
2196     return 0;
2197 }
2198
2199 /*
2200  * Take the defending sector or ship from the defender and give it to the
2201  * attacker.
2202  */
2203
2204 static int
2205 take_def(int combat_mode, struct emp_qelem *list, struct combat *off,
2206          struct combat *def)
2207 {
2208     int n;
2209     int occuppied = 0;
2210     struct llist *llp, *delete_me = 0;
2211     char buf[1024];
2212     struct sctstr sect;
2213     struct shpstr ship;
2214     struct lndstr land;
2215
2216     for (n = 0; n <= off->last && !occuppied; ++n) {
2217         if (off[n].type != EF_BAD &&
2218             off[n].troops > 0 &&
2219             (off[n].type != EF_SECTOR || off[n].mob)) {
2220             ++occuppied;
2221             if (def->type == EF_LAND) {
2222                 if (def->lnd_lcp->l_flags & L_SPY) {
2223                     continue;
2224                 }
2225             }
2226             --(off[n].troops);
2227             --(off[n].mil);
2228             ++def->mil;
2229             pr("1 mil from %s moves %s\n",
2230                prcom(0, off + n), prcom(2, def));
2231         }
2232     }
2233     if (!occuppied) {
2234         if (QEMPTY(list)) {
2235             pr("%s left unoccupied\n", prcom(0, def));
2236             if (def->own)
2237                 wu(0, def->own,
2238                    "No enemy troops moved %s so you still own it!\n",
2239                    pr_com(2, def, def->own));
2240             return 0;
2241         } else {
2242             llp = (struct llist *)list->q_forw;
2243             llp->land.lnd_x = def->x;
2244             llp->land.lnd_y = def->y;
2245             take_move_in_mob(combat_mode, llp, off, def);
2246             if (def->type == EF_SHIP) {
2247                 llp->land.lnd_ship = def->shp_uid;
2248                 sprintf(buf, "boards %s", prcom(0, def));
2249                 delete_me = llp;
2250             } else {
2251                 llp->land.lnd_ship = -1;
2252                 sprintf(buf, "moves in to occupy %s",
2253                         xyas(def->x, def->y, player->cnum));
2254                 lnd_delete(llp, buf);
2255             }
2256         }
2257     }
2258     put_combat(def);
2259     if (def->type == EF_SECTOR) {
2260         getsect(def->x, def->y, &sect);
2261         takeover(&sect, player->cnum);
2262         if (sect.sct_type == SCT_CAPIT || sect.sct_type == SCT_MOUNT)
2263             caploss(&sect, def->own,
2264                     "* We have captured %s's capital, sir! *\n");
2265         putsect(&sect);
2266     } else if (def->type == EF_SHIP) {
2267         getship(def->shp_uid, &ship);
2268         takeover_ship(&ship, player->cnum, 1);
2269         putship(ship.shp_uid, &ship);
2270     } else if (def->type == EF_LAND) {
2271         getland(def->lnd_uid, &land);
2272         takeover_land(&land, player->cnum, 1);
2273         putland(land.lnd_uid, &land);
2274     }
2275     if (delete_me)
2276         lnd_delete(delete_me, buf);
2277     att_get_combat(def, 0);
2278     return 1;
2279 }
2280
2281 /*
2282  * Ask the attacker which mil & land units they'd like to move into the
2283  * conquered sector.
2284  */
2285
2286 static void
2287 ask_move_in(struct combat *off, struct emp_qelem *olist,
2288             struct combat *def)
2289 {
2290     int n;
2291     struct emp_qelem *qp, *next;
2292     struct llist *llp;
2293     char buf[512];
2294     char prompt[512];
2295     char land_answer[256];
2296     char *answerp;
2297
2298     for (n = 0; n <= off->last; ++n)
2299         if (off[n].type != EF_BAD && off[n].troops > 0)
2300             if (off[n].mob) {
2301                 ask_move_in_off(&off[n], def);
2302                 if (player->aborted)
2303                     break;
2304             }
2305
2306     if (QEMPTY(olist))
2307         return;
2308     memset(land_answer, 0, sizeof(land_answer));
2309     for (qp = olist->q_forw; qp != olist; qp = next) {
2310         next = qp->q_forw;
2311         llp = (struct llist *)qp;
2312         answerp = &land_answer[(int)llp->land.lnd_army];
2313         if (player->aborted || att_get_combat(def, 0) < 0)
2314             *answerp = 'N';
2315         if (*answerp == 'Y')
2316             continue;
2317         if (*answerp != 'N') {
2318             if (!get_land(A_ATTACK, def, llp->land.lnd_uid, llp, 0))
2319                 continue;
2320             sprintf(prompt, "Move in with %s (%c %d%%) [ynYNq?] ",
2321                     prland(&llp->land),
2322                     llp->land.lnd_army ? llp->land.lnd_army :  '~',
2323                     llp->land.lnd_effic);
2324             *answerp = att_prompt(prompt, llp->land.lnd_army);
2325             if (player->aborted || att_get_combat(def, 0) < 0)
2326                 *answerp = 'N';
2327             if (!get_land(A_ATTACK, def, llp->land.lnd_uid, llp, 0))
2328                 continue;
2329         }
2330         if (*answerp == 'y' || *answerp == 'Y')
2331             continue;
2332         sprintf(buf, "stays in %s",
2333                 xyas(llp->land.lnd_x, llp->land.lnd_y, player->cnum));
2334         lnd_delete(llp, buf);
2335     }
2336     if (QEMPTY(olist))
2337         return;
2338     if (att_get_combat(def, 0) < 0) {
2339         for (qp = olist->q_forw; qp != olist; qp = next) {
2340             next = qp->q_forw;
2341             llp = (struct llist *)qp;
2342             if (!get_land(A_ATTACK, def, llp->land.lnd_uid, llp, 0))
2343                 continue;
2344             sprintf(buf, "stays in %s",
2345                     xyas(llp->land.lnd_x, llp->land.lnd_y, player->cnum));
2346             lnd_delete(llp, buf);
2347         }
2348         return;
2349     }
2350     if (opt_INTERDICT_ATT)
2351         lnd_interdict(olist, def->x, def->y, player->cnum);
2352     move_in_land(A_ATTACK, off, olist, def);
2353 }
2354
2355 /* Move offensive land units to the conquered sector or ship */
2356
2357 static void
2358 move_in_land(int combat_mode, struct combat *off, struct emp_qelem *olist,
2359              struct combat *def)
2360 {
2361     struct emp_qelem *qp, *next;
2362     struct llist *llp;
2363     char buf[512];
2364
2365     if (QEMPTY(olist))
2366         return;
2367     for (qp = olist->q_forw; qp != olist; qp = next) {
2368         next = qp->q_forw;
2369         llp = (struct llist *)qp;
2370         if (!get_land(combat_mode, def, llp->land.lnd_uid, llp, 0))
2371             continue;
2372         take_move_in_mob(combat_mode, llp, off, def);
2373         llp->land.lnd_x = def->x;
2374         llp->land.lnd_y = def->y;
2375         if (def->type == EF_SHIP)
2376             llp->land.lnd_ship = def->shp_uid;
2377         else
2378             llp->land.lnd_ship = -1;
2379     }
2380     if (QEMPTY(olist))
2381         return;
2382     if (def->type == EF_SECTOR) {
2383         if (opt_INTERDICT_ATT) {
2384             lnd_sweep(olist, 0, 0, def->own);
2385             lnd_check_mines(olist);
2386         }
2387         sprintf(buf, "now occupies %s", prcom(0, def));
2388     } else {
2389         sprintf(buf, "boards %s", prcom(0, def));
2390     }
2391     if (QEMPTY(olist))
2392         return;
2393     for (qp = olist->q_forw; qp != olist; qp = next) {
2394         next = qp->q_forw;
2395         llp = (struct llist *)qp;
2396         lnd_print(llp, buf);
2397     }
2398     if (QEMPTY(olist))
2399         return;
2400     lnd_put(olist, 0);
2401 }
2402
2403 /*
2404  * Move assaulting, paradropping, or boarding mil & units into def
2405  * If the mil are coming from a ship, then pack a lunch.
2406  */
2407
2408 void
2409 att_move_in_off(int combat_mode, struct combat *off,
2410                 struct emp_qelem *olist, struct combat *def)
2411 {
2412     struct sctstr sect;
2413     struct shpstr ship;
2414     int troops, n;
2415     int lunchbox = 0;
2416
2417     move_in_land(combat_mode, off, olist, def);
2418
2419     for (n = 0; n <= off->last; ++n) {
2420         if (off[n].type == EF_BAD || !off[n].troops)
2421             continue;
2422         troops = off[n].troops;
2423         off[n].troops = 0;
2424         off[n].mil -= troops;
2425         def->mil += troops;
2426         put_combat(off + n);
2427         if (combat_mode == A_ASSAULT) {
2428             if (CANT_HAPPEN(off[n].type != EF_SHIP))
2429                 continue;
2430             getship(off[n].shp_uid, &ship);
2431             lunchbox += (int)((troops + 1) * ship.shp_item[I_FOOD]
2432                               / (ship.shp_item[I_MILIT] + troops
2433                                  + ship.shp_item[I_CIVIL] + 0.5));
2434
2435             ship.shp_item[I_FOOD] -= lunchbox;
2436             putship(ship.shp_uid, &ship);
2437         }
2438     }
2439
2440     put_combat(def);
2441
2442     if (combat_mode == A_ASSAULT) {
2443         if (CANT_HAPPEN(def->type != EF_SECTOR))
2444             return;
2445         getsect(def->x, def->y, &sect);
2446         if (lunchbox > ITEM_MAX - sect.sct_item[I_FOOD])
2447             lunchbox = ITEM_MAX - sect.sct_item[I_FOOD];
2448         sect.sct_item[I_FOOD] += lunchbox;
2449         putsect(&sect);
2450     }
2451 }
2452
2453
2454 /* Ask how many mil to move in from each sector */
2455
2456 static void
2457 ask_move_in_off(struct combat *off, struct combat *def)
2458 {
2459     int mob_support;
2460     int num_mil, dam = 0, left;
2461     double d, weight;
2462     char prompt[512];
2463     char buf[1024];
2464     char *p;
2465
2466     if (att_get_combat(off, 0) <= 0)
2467         return;
2468     if (att_get_combat(def, 0) < 0)
2469         return;
2470     if (off->own != player->cnum)
2471         return;
2472     d = att_mobcost(off->own, def, MOB_MOVE);
2473     if ((mob_support = MIN(off->troops, (int)(off->mob / d))) <= 0)
2474         return;
2475     sprintf(prompt, "How many mil to move in from %s (%d max)? ",
2476             xyas(off->x, off->y, player->cnum), mob_support);
2477     if (!(p = getstring(prompt, buf)) || !*p || (num_mil = atoi(p)) <= 0)
2478         return;
2479 /* Make sure we don't move in more than we can support mobility-wise */
2480     if (num_mil > mob_support)
2481         num_mil = mob_support;
2482     if (att_get_combat(off, 0) <= 0)
2483         return;
2484     if (att_get_combat(def, 0) < 0)
2485         return;
2486     if ((num_mil = MIN(off->troops, num_mil)) <= 0) {
2487         pr("No mil moved in from %s\n",
2488            xyas(off->x, off->y, player->cnum));
2489         return;
2490     }
2491     mob_support = MAX(1, (int)(num_mil * d));
2492     off->mob -= MIN(off->mob, mob_support);
2493     off->mil -= num_mil;
2494     off->troops -= num_mil;
2495     put_combat(off);
2496     left = num_mil;
2497     weight = (double)num_mil * ichr[I_MILIT].i_lbs;
2498     if (opt_INTERDICT_ATT && chance(weight / 200.0)) {
2499         if (chance(weight / 100.0))
2500             dam +=
2501                 ground_interdict(def->x, def->y, player->cnum, "military");
2502         dam += check_lmines(def->x, def->y, weight);
2503     }
2504
2505     if (dam) {
2506         left = commdamage(num_mil, dam, I_MILIT);
2507         if (left < num_mil) {
2508             if (left) {
2509                 pr("%d of the mil you were moving were destroyed!\nOnly %d mil made it to %s\n", num_mil - left, left, xyas(def->x, def->y, player->cnum));
2510             } else {
2511                 pr("All of the mil you were moving were destroyed!\n");
2512             }
2513         }
2514         /* maybe got nuked */
2515         if (att_get_combat(def, 0) < 0)
2516             return;
2517     }
2518     def->mil += left;
2519     put_combat(def);
2520 }
2521
2522
2523 /* Charge land units for moving into a sector or onto a ship */
2524
2525 static void
2526 take_move_in_mob(int combat_mode, struct llist *llp, struct combat *off,
2527                  struct combat *def)
2528 {
2529     int mobcost;
2530     int new;
2531
2532     switch (combat_mode) {
2533     case A_ATTACK:
2534         mobcost = lnd_pathcost(&llp->land,
2535                                att_mobcost(off->own, def,
2536                                            lnd_mobtype(&llp->land)));
2537         new = llp->land.lnd_mobil - mobcost;
2538         if (new < -127)
2539             new = -127;
2540         llp->land.lnd_mobil = new;
2541         break;
2542     case A_ASSAULT:
2543         if (off->shp_mcp->m_flags & M_LAND) {
2544             if (llp->lcp->l_flags & L_MARINE)
2545                 llp->land.lnd_mobil -=
2546                     (float)etu_per_update * land_mob_scale * 0.5;
2547             else
2548                 llp->land.lnd_mobil -= (float)etu_per_update * land_mob_scale;
2549         } else {
2550             if (llp->lcp->l_flags & L_MARINE)
2551                 llp->land.lnd_mobil = 0;
2552             else
2553                 llp->land.lnd_mobil = -(float)etu_per_update * land_mob_scale;
2554         }
2555         break;
2556     case A_BOARD:
2557         /* I arbitrarily chose the numbers 10 and 40 below -KHS */
2558         if (llp->lcp->l_flags & L_MARINE)
2559             llp->land.lnd_mobil -= 10;
2560         else
2561             llp->land.lnd_mobil -= 40;
2562         break;
2563     }
2564     llp->land.lnd_harden = 0;
2565 }
2566
2567 static void
2568 free_list(struct emp_qelem *list)
2569 {
2570     struct emp_qelem *qp, *next;
2571
2572     if (!list || QEMPTY(list))
2573         return;
2574
2575     qp = list->q_forw;
2576     while (qp != list) {
2577         next = qp->q_forw;
2578         emp_remque(qp);
2579         free(qp);
2580         qp = next;
2581     }
2582 }
2583
2584 int
2585 att_free_lists(struct emp_qelem *olist, struct emp_qelem *dlist)
2586 {
2587     free_list(olist);
2588     free_list(dlist);
2589     return RET_OK;
2590 }
2591
2592 /*
2593  * sector_strength - Everyone starts at 1.  You can get up to a max
2594  *                   of d_dstr, depending on how much you build up the
2595  *                   defenses of the sector. 
2596  */
2597
2598 double
2599 sector_strength(struct sctstr *sp)
2600 {
2601     double def = SCT_DEFENSE(sp) / 100.0;
2602     double base = sp->sct_type == SCT_MOUNT ? 2.0 : 1.0;
2603     double d = base + (dchr[sp->sct_type].d_dstr - base) * def;
2604
2605     if (d > dchr[sp->sct_type].d_dstr)
2606         d = dchr[sp->sct_type].d_dstr;
2607     if (d < base)
2608         d = base;
2609     return d;
2610 }