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