]> git.pond.sub.org Git - empserver/blob - src/lib/commands/bomb.c
4179ea328bf570ecd7a8e2e71036a7aadb7ffe9a
[empserver] / src / lib / commands / bomb.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2009, 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  *  bomb.c: Fly bombing missions
29  *
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Ken Stevens, 1995
33  *     Steve McClure, 1998-2000
34  *     Markus Armbruster, 2004-2009
35  */
36
37 #include <config.h>
38
39 #include <ctype.h>
40 #include "commands.h"
41 #include "damage.h"
42 #include "item.h"
43 #include "land.h"
44 #include "news.h"
45 #include "nuke.h"
46 #include "optlist.h"
47 #include "path.h"
48 #include "plane.h"
49 #include "retreat.h"
50 #include "ship.h"
51
52 static void pin_bomb(struct emp_qelem *, struct sctstr *);
53 static void eff_bomb(struct emp_qelem *, struct sctstr *);
54 static void comm_bomb(struct emp_qelem *, struct sctstr *);
55 static void ship_bomb(struct emp_qelem *, struct sctstr *);
56 static void plane_bomb(struct emp_qelem *, struct sctstr *);
57 static void land_bomb(struct emp_qelem *, struct sctstr *);
58 static void strat_bomb(struct emp_qelem *, struct sctstr *);
59 static int changed_plane_aborts(struct plist *);
60 static int pinflak_planedamage(struct plnstr *, struct plchrstr *,
61                                natid, int);
62
63 static i_type bombcomm[] = {
64     I_CIVIL,
65     I_MILIT,
66     I_SHELL,
67     I_GUN,
68     I_PETROL,
69     I_IRON,
70     I_DUST,
71     I_BAR,
72     I_FOOD,
73     I_OIL,
74     I_LCM,
75     I_HCM,
76     I_UW,
77     I_RAD
78 };
79 static int nbomb = sizeof(bombcomm) / sizeof(*bombcomm);
80
81 int
82 bomb(void)
83 {
84     char *p;
85     coord tx, ty;
86     coord ax, ay;
87     int ap_to_target;
88     char flightpath[MAX_PATH_LEN];
89     struct nstr_item ni_bomb;
90     struct nstr_item ni_esc;
91     struct sctstr target;
92     struct emp_qelem bomb_list;
93     struct emp_qelem esc_list;
94     int wantflags;
95     struct sctstr ap_sect;
96     char mission;
97     struct plist *plp;
98     struct emp_qelem *qp, *next;
99     int rel;
100     struct natstr *natp;
101     char buf[1024];
102
103     wantflags = 0;
104     if (get_planes(&ni_bomb, &ni_esc, player->argp[1], player->argp[2]) < 0)
105         return RET_SYN;
106     p = getstarg(player->argp[3], "pinpoint, or strategic? ", buf);
107     if (!p || !*p)
108         return RET_SYN;
109     mission = *p;
110     if (!strchr("ps", mission))
111         return RET_SYN;
112     if (!get_assembly_point(player->argp[4], &ap_sect, buf))
113         return RET_SYN;
114     ax = ap_sect.sct_x;
115     ay = ap_sect.sct_y;
116     if (!getpath(flightpath, player->argp[5], ax, ay, 0, 0, P_FLYING)
117         || *flightpath == 0)
118         return RET_SYN;
119     tx = ax;
120     ty = ay;
121     (void)pathtoxy(flightpath, &tx, &ty, fcost);
122     pr("target sector is %s\n", xyas(tx, ty, player->cnum));
123     getsect(tx, ty, &target);
124     ap_to_target = strlen(flightpath);
125     if (flightpath[ap_to_target - 1] == 'h')
126         ap_to_target--;
127     pr("range to target is %d\n", ap_to_target);
128     /*
129      * select planes within range
130      */
131     pln_sel(&ni_bomb, &bomb_list, &ap_sect, ap_to_target,
132             2, wantflags, P_M | P_O);
133     pln_sel(&ni_esc, &esc_list, &ap_sect, ap_to_target,
134             2, wantflags | P_F | P_ESC, P_M | P_O);
135     /*
136      * now arm and equip the bombers, transports, whatever.
137      */
138     pln_arm(&bomb_list, 2 * ap_to_target, mission, NULL);
139     if (QEMPTY(&bomb_list)) {
140         pr("No planes could be equipped for the mission.\n");
141         return RET_FAIL;
142     }
143     pln_arm(&esc_list, 2 * ap_to_target, 'e', NULL);
144     ac_encounter(&bomb_list, &esc_list, ax, ay, flightpath, 0);
145     if (QEMPTY(&bomb_list)) {
146         pr("No planes got through fighter defenses\n");
147     } else if (target.sct_type == SCT_SANCT) {
148         pr("You can't bomb that sector!\n");
149     } else {
150         switch (mission) {
151         case 'p':
152             pin_bomb(&bomb_list, &target);
153             for (qp = bomb_list.q_forw; qp != &bomb_list; qp = next) {
154                 next = qp->q_forw;
155                 plp = (struct plist *)qp;
156                 changed_plane_aborts(plp);
157             }
158             for (qp = esc_list.q_forw; qp != &esc_list; qp = next) {
159                 next = qp->q_forw;
160                 plp = (struct plist *)qp;
161                 changed_plane_aborts(plp);
162             }
163             break;
164         case 's':
165             if (opt_SLOW_WAR) {
166                 natp = getnatp(player->cnum);
167                 if (target.sct_own) {
168                     rel = getrel(natp, target.sct_own);
169                     if ((rel != AT_WAR) && (player->cnum != target.sct_own)
170                         && (target.sct_own) &&
171                         (target.sct_oldown != player->cnum)) {
172                         pr("You're not at war with them!\n");
173                         pln_put(&bomb_list);
174                         pln_put(&esc_list);
175                         return RET_FAIL;
176                     }
177                 }
178             }
179             nreport(player->cnum, N_SCT_BOMB, target.sct_own, 1);
180             strat_bomb(&bomb_list, &target);
181             break;
182         default:
183             CANT_REACH();
184         }
185     }
186     pln_put(&bomb_list);
187     pln_put(&esc_list);
188     return RET_OK;
189 }
190
191 static void
192 pin_bomb(struct emp_qelem *list, struct sctstr *target)
193 {
194     struct dchrstr *dcp;
195     int nplanes;
196     int nships;
197     int type;
198     int bad;
199     char *p;
200     int nsubs;
201     int nunits;
202     struct natstr *natp;
203     int rel;
204     char buf[1024];
205     int i;
206
207     bad = 0;
208     type = target->sct_type;
209     dcp = &dchr[type];
210     pr("Target sector is a %s constructed %s\n",
211        effadv((int)target->sct_effic), dcp->d_name);
212     nsubs = 0;
213     nships = shipsatxy(target->sct_x, target->sct_y, 0, M_SUB, 0);
214     if (pln_caps(list) & P_A) {
215         nsubs = shipsatxy(target->sct_x, target->sct_y, M_SUB, 0, 1);
216         if (nsubs > 0)
217             pr("Some subs are present in the sector.\n");
218     }
219     nplanes = planesatxy(target->sct_x, target->sct_y, 0, 0);
220     nunits = unitsatxy(target->sct_x, target->sct_y, 0, 0);
221   retry:
222     p = getstring("Bomb what? (ship, plane, land unit, efficiency, commodities) ",
223                   buf);
224     if (!p)
225        return;
226     if (!*p) {
227         bad++;
228         if (bad > 2)
229             return;
230         goto retry;
231     }
232     switch (*p) {
233     case 'l':
234         if (opt_SLOW_WAR) {
235             natp = getnatp(player->cnum);
236             if (target->sct_own) {
237                 rel = getrel(natp, target->sct_own);
238                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
239                     && (target->sct_own) &&
240                     (target->sct_oldown != player->cnum)) {
241                     pr("You're not at war with them!\n");
242                     goto retry;
243                 }
244             }
245         }
246         if (nunits == 0) {
247             pr("no units there\n");
248             goto retry;
249         }
250         land_bomb(list, target);
251         break;
252     case 'p':
253         if (opt_SLOW_WAR) {
254             natp = getnatp(player->cnum);
255             if (target->sct_own) {
256                 rel = getrel(natp, target->sct_own);
257                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
258                     && (target->sct_own) &&
259                     (target->sct_oldown != player->cnum)) {
260                     pr("You're not at war with them!\n");
261                     goto retry;
262                 }
263             }
264         }
265         if (nplanes == 0) {
266             pr("no planes there\n");
267             goto retry;
268         }
269         plane_bomb(list, target);
270         break;
271     case 's':
272         if (nships == 0) {
273             if (pln_caps(list) & P_A) {
274                 if (nsubs == 0) {
275                     pr("no ships there\n");
276                     goto retry;
277                 }
278             } else {
279                 pr("no ships there\n");
280                 goto retry;
281             }
282         }
283         ship_bomb(list, target);
284         break;
285     case 'c':
286         if (opt_SLOW_WAR) {
287             natp = getnatp(player->cnum);
288             if (target->sct_own) {
289                 rel = getrel(natp, target->sct_own);
290                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
291                     && (target->sct_own) &&
292                     (target->sct_oldown != player->cnum)) {
293                     pr("You're not at war with them!\n");
294                     goto retry;
295                 }
296             }
297         }
298
299         for (i = 0; i < nbomb; i++) {
300             if (!target->sct_item[bombcomm[i]])
301                 continue;
302             break;
303         }
304         if (i >= nbomb) {
305             pr("No bombable commodities in %s\n",
306                xyas(target->sct_x, target->sct_y, player->cnum));
307             goto retry;
308         }
309         comm_bomb(list, target);
310         break;
311     case 'e':
312         if (opt_SLOW_WAR) {
313             natp = getnatp(player->cnum);
314             if (target->sct_own) {
315                 rel = getrel(natp, target->sct_own);
316                 if ((rel != AT_WAR) && (player->cnum != target->sct_own)
317                     && (target->sct_own) &&
318                     (target->sct_oldown != player->cnum)) {
319                     pr("You're not at war with them!\n");
320                     goto retry;
321                 }
322             }
323         }
324         eff_bomb(list, target);
325         break;
326     case 'q':
327         pr("Aborting mission.\n");
328         return;
329     default:
330         pr("Bad target type.\n");
331         goto retry;
332     }
333 }
334
335 static void
336 eff_bomb(struct emp_qelem *list, struct sctstr *target)
337 {
338     struct plist *plp;
339     struct emp_qelem *qp, *next;
340     struct sctstr sect;
341     int oldeff, dam = 0;
342     int nukedam;
343
344     for (qp = list->q_forw; qp != list; qp = next) {
345         next = qp->q_forw;
346         plp = (struct plist *)qp;
347         if (changed_plane_aborts(plp))
348             continue;
349         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
350             continue;
351         if (plp->bombs || nuk_on_plane(&plp->plane) >= 0)
352             dam += pln_damage(&plp->plane, target->sct_x, target->sct_y,
353                               'p', &nukedam, 1);
354     }
355     if (dam <= 0)               /* dam == 0 if only nukes were delivered */
356         return;
357     getsect(target->sct_x, target->sct_y, &sect);
358     target = &sect;
359     oldeff = target->sct_effic;
360     target->sct_effic = effdamage(target->sct_effic, dam);
361     target->sct_avail = effdamage(target->sct_avail, dam);
362     target->sct_road = effdamage(target->sct_road, dam);
363     target->sct_rail = effdamage(target->sct_rail, dam);
364     target->sct_defense = effdamage(target->sct_defense, dam);
365     pr("did %d%% damage to efficiency in %s\n",
366        oldeff - target->sct_effic,
367        xyas(target->sct_x, target->sct_y, player->cnum));
368     if (target->sct_own)
369         wu(0, target->sct_own,
370            "%s bombing raid did %d%% damage in %s\n",
371            cname(player->cnum), oldeff - target->sct_effic,
372            xyas(target->sct_x, target->sct_y, target->sct_own));
373     bridge_damaged(target);
374     putsect(&sect);
375     collateral_damage(target->sct_x, target->sct_y, dam);
376 }
377
378 static void
379 comm_bomb(struct emp_qelem *list, struct sctstr *target)
380 {
381     struct plist *plp;
382     double b;
383     int i;
384     int amt, before;
385     struct ichrstr *ip;
386     struct emp_qelem *qp, *next;
387     struct sctstr sect;
388     int dam = 0;
389     int nukedam;
390
391     for (i = 0; i < nbomb; i++) {
392         if (target->sct_item[bombcomm[i]] == 0)
393             continue;
394         if (opt_SUPER_BARS && bombcomm[i] == I_BAR)
395             continue;
396         ip = &ichr[bombcomm[i]];
397         pr("some %s\n", ip->i_name);
398     }
399     for (;;) {
400         ip = whatitem(NULL, "commodity to bomb? ");
401         if (player->aborted)
402             return;
403         if (!ip)
404             continue;
405
406         for (i = 0; i < nbomb; i++) {
407             if (opt_SUPER_BARS && bombcomm[i] == I_BAR)
408                 continue;
409             if (&ichr[bombcomm[i]] == ip)
410                 break;
411         }
412         if (i == nbomb) {
413             pr("You can't bomb %s!\n", ip->i_name);
414             for (i = 0; i < nbomb; i++) {
415                 if (opt_SUPER_BARS && bombcomm[i] == I_BAR)
416                     continue;
417                 pr("%s%s", i == 0 ? "Bombable: " : ", ",
418                    ichr[bombcomm[i]].i_name);
419             }
420             pr("\n");
421         } else
422             break;
423     }
424     for (qp = list->q_forw; qp != list; qp = next) {
425         next = qp->q_forw;
426         plp = (struct plist *)qp;
427         if (changed_plane_aborts(plp))
428             continue;
429         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
430             continue;
431         if (plp->bombs || nuk_on_plane(&plp->plane) >= 0)
432             dam += pln_damage(&plp->plane, target->sct_x, target->sct_y,
433                               'p', &nukedam, 1);
434     }
435     if (dam <= 0)               /* dam == 0 if only nukes were delivered */
436         return;
437     getsect(target->sct_x, target->sct_y, &sect);
438     target = &sect;
439     before = target->sct_item[ip->i_uid];
440     target->sct_item[ip->i_uid] = amt = commdamage(before, dam, ip->i_uid);
441     if (before > 0.0)
442         b = 100.0 * (1.0 - (double)amt / (double)before);
443     else
444         b = 0.0;
445     pr("did %.2f%% damage to %s in %s\n",
446        b, ip->i_name, xyas(target->sct_x, target->sct_y, player->cnum));
447     nreport(player->cnum, N_SCT_BOMB, target->sct_own, 1);
448     if (target->sct_own != 0)
449         wu(0, target->sct_own,
450            "%s precision bombing raid did %.2f%% damage to %s in %s\n",
451            cname(player->cnum), b, ip->i_name,
452            xyas(target->sct_x, target->sct_y, target->sct_own));
453     putsect(&sect);
454     collateral_damage(target->sct_x, target->sct_y, dam);
455 }
456
457 static void
458 ship_bomb(struct emp_qelem *list, struct sctstr *target)
459 {
460     struct plist *plp;
461     struct mchrstr *mcp;
462     int dam;
463     char *q;
464     int n;
465     struct emp_qelem *qp, *next;
466     int shipno;
467     struct shpstr ship;
468     int nships = 0;
469     struct shiplist *head = NULL;
470     char buf[1024];
471     char prompt[128];
472     int hitchance;
473     int nukedam;
474     int flak;
475     int gun;
476
477     for (qp = list->q_forw; qp != list; qp = next) {
478         next = qp->q_forw;
479         free_shiplist(&head);
480         plp = (struct plist *)qp;
481         if (changed_plane_aborts(plp))
482             continue;
483         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
484             continue;
485         if (plp->pcp->pl_flags & P_A)
486             nships = asw_shipsatxy(target->sct_x, target->sct_y, 0, 0,
487                                    &plp->plane, &head);
488         else
489             nships = shipsatxy(target->sct_x, target->sct_y, 0, M_SUB, 0);
490         if (nships == 0) {
491             pr("%s could not find any ships!\n", prplane(&plp->plane));
492             continue;
493         }
494         (void)sprintf(prompt, "%s, %d bombs.  Target ('~' to skip)? ",
495                       prplane(&plp->plane), plp->bombs);
496         shipno = -1;
497         while (shipno < 0) {
498             if (!(q = getstring(prompt, buf)))
499                 goto out;
500             if (*q == 0)
501                 continue;
502             if (*q == '~')
503                 break;
504             if (*q == '?') {
505                 if (plp->pcp->pl_flags & P_A)
506                     print_shiplist(head);
507                 else
508                     shipsatxy(target->sct_x, target->sct_y, 0, M_SUB, 0);
509                 continue;
510             }
511             n = atoi(q);
512             if (n < 0)
513                 continue;
514             if ((!(plp->pcp->pl_flags & P_A) || on_shiplist(n, head)) &&
515                 getship(n, &ship) && ship.shp_own &&
516                 ship.shp_x == target->sct_x && ship.shp_y == target->sct_y)
517                 shipno = n;
518             else
519                 pr("Ship #%d not spotted\n", n);
520         }
521         if (shipno < 0)
522             continue;
523         if ((plp->pcp->pl_flags & P_A) && !on_shiplist(shipno, head))
524             continue;
525         if (changed_plane_aborts(plp))
526             continue;
527
528         gun = shp_usable_guns(&ship);
529         mcp = &mchr[(int)ship.shp_type];
530         if (gun > 0 && !(mcp->m_flags & M_SUB)) {
531             flak = (int)(techfact(ship.shp_tech, gun) * 2.0);
532             PR(ship.shp_own, "Flak! Firing %d guns from ship %s\n",
533                flak, prship(&ship));
534             if (pinflak_planedamage(&plp->plane, plp->pcp, ship.shp_own, flak))
535                 continue;
536         }
537
538         dam = 0;
539         if (nuk_on_plane(&plp->plane) >= 0)
540             hitchance = 100;
541         else {
542             hitchance = pln_hitchance(&plp->plane,
543                                       shp_hardtarget(&ship), EF_SHIP);
544             pr("%d%% hitchance...", hitchance);
545         }
546         if (roll(100) <= hitchance) {
547             /* pinbombing is more accurate than normal bombing */
548             dam = 2 * pln_damage(&plp->plane, target->sct_x, target->sct_y,
549                                  'p', &nukedam, 1);
550         } else {
551             pr("splash\n");
552             /* Bombs that miss have to land somewhere! */
553             dam = pln_damage(&plp->plane, target->sct_x, target->sct_y,
554                              'p', &nukedam, 0);
555             collateral_damage(target->sct_x, target->sct_y, dam);
556             dam = 0;
557         }
558         if (dam <= 0)           /* dam == 0 if only nukes were delivered */
559             continue;
560         if (mcp->m_flags & M_SUB)
561             nreport(player->cnum, N_SUB_BOMB, ship.shp_own, 1);
562         else
563             nreport(player->cnum, N_SHP_BOMB, ship.shp_own, 1);
564         if (ship.shp_own) {
565             wu(0, ship.shp_own, "%s bombs did %d damage to %s at %s\n",
566                cname(player->cnum), dam,
567                prship(&ship),
568                xyas(target->sct_x, target->sct_y, ship.shp_own));
569         }
570         pr("\n");
571         check_retreat_and_do_shipdamage(&ship, dam);
572         if (ship.shp_rflags & RET_BOMBED)
573             if (((ship.shp_rflags & RET_INJURED) == 0) || !dam)
574                 retreat_ship(&ship, 'b');
575         putship(ship.shp_uid, &ship);
576         getship(ship.shp_uid, &ship);
577         if (!ship.shp_own) {
578             pr("%s at %s sunk!\n",
579                prship(&ship),
580                xyas(target->sct_x, target->sct_y, player->cnum));
581         }
582         collateral_damage(target->sct_x, target->sct_y, dam / 2);
583     }
584 out:
585     free_shiplist(&head);
586 }
587
588 static void
589 plane_bomb(struct emp_qelem *list, struct sctstr *target)
590 {
591     int dam;
592     char *q;
593     int n;
594     natid own;
595     struct plnstr plane;
596     struct emp_qelem *qp, *next;
597     int planeno;
598     struct plist *plp;
599     char prompt[128];
600     char buf[1024];
601     int hitchance;
602     int nukedam;
603     int nplanes;
604
605     for (qp = list->q_forw; qp != list; qp = next) {
606         next = qp->q_forw;
607         plp = (struct plist *)qp;
608         if (changed_plane_aborts(plp))
609             continue;
610         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
611             continue;
612         nplanes = planesatxy(target->sct_x, target->sct_y, 0, 0);
613         if (nplanes == 0) {
614             pr("%s could not find any planes!\n", prplane(&plp->plane));
615             continue;
616         }
617         (void)sprintf(prompt, "%s, %d bombs.  Target ('~' to skip)? ",
618                       prplane(&plp->plane), plp->bombs);
619         planeno = -1;
620         while (planeno < 0) {
621             if (!(q = getstring(prompt, buf)))
622                 return;
623             if (*q == 0)
624                 continue;
625             if (*q == '~')
626                 break;
627             if (*q == '?') {
628                 planesatxy(target->sct_x, target->sct_y, 0, 0);
629                 continue;
630             }
631             n = atoi(q);
632             if (n < 0)
633                 continue;
634             if (getplane(n, &plane) &&
635                 plane.pln_x == target->sct_x &&
636                 plane.pln_y == target->sct_y &&
637                 plane.pln_ship < 0 && plane.pln_land < 0 &&
638                 !(plane.pln_flags & PLN_LAUNCHED))
639                 planeno = n;
640             else
641                 pr("Plane #%d not spotted\n", n);
642         }
643         if (planeno < 0)
644             continue;
645         if (changed_plane_aborts(plp))
646             continue;
647         dam = 0;
648         if (nuk_on_plane(&plp->plane) >= 0)
649             hitchance = 100;
650         else {
651             hitchance = pln_hitchance(&plp->plane, 0, EF_PLANE);
652             pr("%d%% hitchance...", hitchance);
653         }
654         if (roll(100) <= hitchance) {
655             /* pinbombing is more accurate than normal bombing */
656             dam = 2 * pln_damage(&plp->plane, target->sct_x, target->sct_y,
657                                  'p', &nukedam, 1);
658         } else {
659             pr("thud\n");
660             /* Bombs that miss have to land somewhere! */
661             dam = pln_damage(&plp->plane, target->sct_x, target->sct_y,
662                              'p', &nukedam, 0);
663             collateral_damage(target->sct_x, target->sct_y, dam);
664             dam = 0;
665         }
666         if (dam <= 0)           /* dam == 0 if only nukes were delivered */
667             continue;
668         if (dam > 100)
669             dam = 100;
670         own = plane.pln_own;
671         if (dam > plane.pln_effic)
672             plane.pln_effic = 0;
673         else
674             plane.pln_effic -= dam;
675         plane.pln_mobil = (dam * plane.pln_mobil / 100.0);
676         if (own == player->cnum) {
677             pr("%s reports %d%% damage\n", prplane(&plane), dam);
678         } else {
679             if (own != 0)
680                 wu(0, own,
681                    "%s pinpoint bombing raid did %d%% damage to %s\n",
682                    cname(player->cnum), dam, prplane(&plane));
683         }
684         nreport(player->cnum, N_DOWN_PLANE, own, 1);
685         if (own != 0)
686             wu(0, own, "%s bombs did %d%% damage to %s at %s\n",
687                cname(player->cnum), dam, prplane(&plane),
688                xyas(target->sct_x, target->sct_y, own));
689         putplane(plane.pln_uid, &plane);
690         collateral_damage(target->sct_x, target->sct_y, dam);
691     }
692 }
693
694 static void
695 land_bomb(struct emp_qelem *list, struct sctstr *target)
696 {
697     int dam;
698     char *q;
699     int n;
700     natid own;
701     char prompt[128];
702     char buf[1024];
703     struct lndstr land;
704     struct emp_qelem *qp, *next;
705     int unitno;
706     int aaf, flak, hitchance;
707     struct plist *plp;
708     int nukedam;
709     int nunits;
710
711     for (qp = list->q_forw; qp != list; qp = next) {
712         next = qp->q_forw;
713         plp = (struct plist *)qp;
714         if (changed_plane_aborts(plp))
715             continue;
716         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
717             continue;
718         nunits = unitsatxy(target->sct_x, target->sct_y, 0, 0);
719         if (nunits == 0) {
720             pr("%s could not find any units!\n", prplane(&plp->plane));
721             continue;
722         }
723         (void)sprintf(prompt, "%s, %d bombs.  Target ('~' to skip)? ",
724                       prplane(&plp->plane), plp->bombs);
725         unitno = -1;
726         while (unitno < 0) {
727             if (!(q = getstring(prompt, buf)))
728                 return;
729             if (*q == 0)
730                 continue;
731             if (*q == '~')
732                 break;
733             if (*q == '?') {
734                 unitsatxy(target->sct_x, target->sct_y, 0, 0);
735                 continue;
736             }
737             n = atoi(q);
738             if (n < 0)
739                 continue;
740             if (getland(n, &land) && land.lnd_own &&
741                 land.lnd_ship < 0 && land.lnd_land < 0 &&
742                 land.lnd_x == target->sct_x && land.lnd_y == target->sct_y)
743                 unitno = n;
744             else
745                 pr("Unit #%d not spotted\n", n);
746         }
747         if (unitno < 0)
748             continue;
749         if (changed_plane_aborts(plp))
750             continue;
751
752         aaf = lnd_aaf(&land);
753         if (aaf) {
754             flak = roundavg(techfact(land.lnd_tech,
755                                      aaf * 3.0 * land.lnd_effic / 100.0));
756             PR(land.lnd_own,
757                "Flak! Firing flak guns from unit %s (aa rating %d)\n",
758                prland(&land), aaf);
759             if (pinflak_planedamage(&plp->plane, plp->pcp, land.lnd_own, flak))
760                 continue;
761         }
762
763         dam = 0;
764         if (nuk_on_plane(&plp->plane) >= 0)
765             hitchance = 100;
766         else {
767             hitchance = pln_hitchance(&plp->plane,
768                                       lnd_hardtarget(&land), EF_LAND);
769             pr("%d%% hitchance...", hitchance);
770         }
771         if (roll(100) <= hitchance) {
772             dam = 2 * pln_damage(&plp->plane, target->sct_x, target->sct_y,
773                                  'p', &nukedam, 1);
774         } else {
775             pr("thud\n");
776             /* Bombs that miss have to land somewhere! */
777             dam = pln_damage(&plp->plane, target->sct_x, target->sct_y,
778                              'p', &nukedam, 0);
779             collateral_damage(target->sct_x, target->sct_y, dam);
780             dam = 0;
781         }
782         if (dam <= 0)           /* dam == 0 if only nukes were delivered */
783             continue;
784         if (dam > 100)
785             dam = 100;
786         own = land.lnd_own;
787         mpr(own, "%s pinpoint bombing raid did %d damage to %s\n",
788             cname(player->cnum), dam, prland(&land));
789         check_retreat_and_do_landdamage(&land, dam);
790
791         if (land.lnd_rflags & RET_BOMBED)
792             if (((land.lnd_rflags & RET_INJURED) == 0) || !dam)
793                 retreat_land(&land, 'b');
794         nreport(player->cnum, N_UNIT_BOMB, own, 1);
795         putland(land.lnd_uid, &land);
796         collateral_damage(target->sct_x, target->sct_y, dam);
797     }
798 }
799
800 static void
801 strat_bomb(struct emp_qelem *list, struct sctstr *target)
802 {
803     struct plist *plp;
804     int dam = 0;
805     struct emp_qelem *qp;
806     struct sctstr sect;
807     int nukedam;
808
809     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
810         plp = (struct plist *)qp;
811         if ((plp->pcp->pl_flags & P_C) && (!(plp->pcp->pl_flags & P_T)))
812             continue;
813         if (plp->bombs || nuk_on_plane(&plp->plane) >= 0)
814             dam += pln_damage(&plp->plane, target->sct_x, target->sct_y,
815                               's', &nukedam, 1);
816     }
817     if (dam <= 0)               /* dam == 0 if only nukes were delivered */
818         return;
819     getsect(target->sct_x, target->sct_y, &sect);
820     target = &sect;
821     if (target->sct_own)
822         wu(0, target->sct_own, "%s bombing raid did %d damage in %s\n",
823            cname(player->cnum), PERCENT_DAMAGE(dam),
824            xyas(target->sct_x, target->sct_y, target->sct_own));
825
826     sectdamage(target, dam);
827
828     pr("did %d damage in %s\n", PERCENT_DAMAGE(dam),
829        xyas(target->sct_x, target->sct_y, player->cnum));
830     putsect(&sect);
831 }
832
833 static int
834 changed_plane_aborts(struct plist *plp)
835 {
836     if (check_plane_ok(&plp->plane))
837         return 0;
838     getplane(plp->plane.pln_uid, &plp->plane);
839     pln_put1(plp);
840     return 1;
841 }
842
843 static int
844 pinflak_planedamage(struct plnstr *pp, struct plchrstr *pcp, natid from,
845                     int flak)
846 {
847     int disp;
848     char dmess[255];
849     int eff;
850     natid plane_owner;
851     int dam;
852
853     dam = ac_flak_dam(flak, pln_def(pp), pcp->pl_flags);
854     disp = 0;
855     plane_owner = pp->pln_own;
856     eff = pp->pln_effic;
857     if (dam <= 0)
858         return 0;
859     memset(dmess, 0, sizeof(dmess));
860     eff -= dam;
861     if (eff < 0)
862         eff = 0;
863     if (eff < PLANE_MINEFF) {
864         sprintf(dmess, " -- shot down");
865         disp = 1;
866     } else if (chance((100 - eff) / 100.0)) {
867         sprintf(dmess, " -- aborted with %d%% damage", 100 - eff);
868         disp = 2;
869     }
870     PR(plane_owner, "    Flak! %s %s takes %d%s.\n",
871        cname(pp->pln_own), prplane(pp), dam, dmess);
872
873     pp->pln_effic = eff;
874     if (disp == 1) {
875         if (from != 0)
876             nreport(from, N_DOWN_PLANE, pp->pln_own, 1);
877     }
878     putplane(pp->pln_uid, pp);
879
880     if (disp > 0)
881         return 1;
882     return 0;
883 }