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