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